Merge branch 'main' into move-apub-context

move-apub-context
Felix Ableitner 5 months ago
commit 77a9f968d7

@ -100,6 +100,9 @@ steps:
region: us-east-1
cache_key: "rust-cache"
path-style: true
backend_operation_timeout: 15m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
@ -226,6 +229,9 @@ steps:
cache_key: "rust-cache"
region: us-east-1
path-style: true
backend_operation_timeout: 60m
compression_level: 0
exit_code: true
mount:
- ".cargo_home"
- "target"
@ -262,6 +268,19 @@ steps:
when:
event: cron
# using https://github.com/pksunkara/cargo-workspaces
publish_to_crates_io:
image: *rust_image
commands:
- 'echo "pub const VERSION: &str = \"$(git describe --tag)\";" > "crates/utils/src/version.rs"'
- cargo install cargo-workspaces
- cp -r migrations crates/db_schema/
- cargo login "$CARGO_API_TOKEN"
- cargo workspaces publish --from-git --allow-dirty --no-verify --allow-branch "${CI_COMMIT_TAG}" --yes custom "${CI_COMMIT_TAG}"
secrets: [cargo_api_token]
when:
event: tag
notify_on_failure:
image: alpine:3
commands:

231
Cargo.lock generated

@ -10,9 +10,9 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
[[package]]
name = "activitypub_federation"
version = "0.5.0-beta.7"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b79bec8d9343c3bc849a4b95266aa3aebb9e235aa4ff7eb6138c6ec16746c2"
checksum = "8bac58c1d61b6e2358adbd043c78ba853428102b489acb7b6cb74ee6f2ae668f"
dependencies = [
"activitystreams-kinds",
"actix-web",
@ -106,9 +106,9 @@ dependencies = [
[[package]]
name = "actix-http"
version = "3.4.0"
version = "3.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a92ef85799cba03f76e4f7c10f533e66d87c9a7e7055f3391f09000ad8351bc9"
checksum = "129d4c88e98860e1758c5de288d1632b07970a16d59bdf7b8d66053d582bb71f"
dependencies = [
"actix-codec",
"actix-rt",
@ -151,7 +151,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb"
dependencies = [
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -261,9 +261,9 @@ dependencies = [
[[package]]
name = "actix-web"
version = "4.4.0"
version = "4.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e4a5b5e29603ca8c94a77c65cf874718ceb60292c5a5c3e5f4ace041af462b9"
checksum = "e43428f3bf11dee6d166b00ec2df4e3aa8cc1606aaa0b7433c146852e2f4e03b"
dependencies = [
"actix-codec",
"actix-http",
@ -309,7 +309,7 @@ dependencies = [
"actix-router",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -527,18 +527,18 @@ checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
name = "async-trait"
version = "0.1.74"
version = "0.1.77"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a66537f1bb974b254c98ed142ff995236e81b9d0fe4db0575f46612cb15eb0f9"
checksum = "c980ee35e870bd1a4d2c8294d4c04d0499e67bca1e4b5cefcc693c2fa00caea9"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -911,7 +911,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1256,7 +1256,7 @@ dependencies = [
"proc-macro2",
"quote",
"strsim",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1289,7 +1289,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
dependencies = [
"darling_core 0.20.3",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1454,7 +1454,7 @@ dependencies = [
"heck",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1465,7 +1465,7 @@ checksum = "c7267437d5b12df60ae29bd97f8d120f1c3a6272d6f213551afa56bbb2ecfbb7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1477,7 +1477,7 @@ dependencies = [
"diesel_table_macro_syntax",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1507,9 +1507,15 @@ version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc5557efc453706fed5e4fa85006fe9817c224c3f480a34c7e5959fd700921c5"
dependencies = [
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "digest"
version = "0.10.7"
@ -1693,7 +1699,7 @@ checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -1881,9 +1887,9 @@ dependencies = [
[[package]]
name = "futures"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335"
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
dependencies = [
"futures-channel",
"futures-core",
@ -1896,9 +1902,9 @@ dependencies = [
[[package]]
name = "futures-channel"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb"
checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78"
dependencies = [
"futures-core",
"futures-sink",
@ -1906,15 +1912,15 @@ dependencies = [
[[package]]
name = "futures-core"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c"
checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d"
[[package]]
name = "futures-executor"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc"
checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d"
dependencies = [
"futures-core",
"futures-task",
@ -1923,38 +1929,38 @@ dependencies = [
[[package]]
name = "futures-io"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa"
checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1"
[[package]]
name = "futures-macro"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb"
checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
name = "futures-sink"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817"
checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5"
[[package]]
name = "futures-task"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2"
checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004"
[[package]]
name = "futures-util"
version = "0.3.29"
version = "0.3.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104"
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
dependencies = [
"futures-channel",
"futures-core",
@ -2523,7 +2529,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lemmy_api"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2540,6 +2546,7 @@ dependencies = [
"lemmy_db_views_actor",
"lemmy_db_views_moderator",
"lemmy_utils",
"pretty_assertions",
"serial_test",
"sitemap-rs",
"tokio",
@ -2551,7 +2558,7 @@ dependencies = [
[[package]]
name = "lemmy_api_common"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2568,6 +2575,7 @@ dependencies = [
"lemmy_utils",
"once_cell",
"percent-encoding",
"pretty_assertions",
"regex",
"reqwest",
"reqwest-middleware",
@ -2585,7 +2593,7 @@ dependencies = [
[[package]]
name = "lemmy_api_crud"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2607,7 +2615,7 @@ dependencies = [
[[package]]
name = "lemmy_apub"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2629,6 +2637,7 @@ dependencies = [
"lemmy_utils",
"moka",
"once_cell",
"pretty_assertions",
"reqwest",
"reqwest-middleware",
"serde",
@ -2646,7 +2655,7 @@ dependencies = [
[[package]]
name = "lemmy_db_schema"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"async-trait",
@ -2662,6 +2671,7 @@ dependencies = [
"futures-util",
"lemmy_utils",
"once_cell",
"pretty_assertions",
"regex",
"rustls 0.21.10",
"serde",
@ -2682,7 +2692,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"actix-web",
"chrono",
@ -2691,6 +2701,7 @@ dependencies = [
"diesel_ltree",
"lemmy_db_schema",
"lemmy_utils",
"pretty_assertions",
"serde",
"serde_with",
"serial_test",
@ -2701,12 +2712,13 @@ dependencies = [
[[package]]
name = "lemmy_db_views_actor"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"chrono",
"diesel",
"diesel-async",
"lemmy_db_schema",
"pretty_assertions",
"serde",
"serde_with",
"serial_test",
@ -2718,7 +2730,7 @@ dependencies = [
[[package]]
name = "lemmy_db_views_moderator"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"diesel",
"diesel-async",
@ -2730,7 +2742,7 @@ dependencies = [
[[package]]
name = "lemmy_federate"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"anyhow",
@ -2753,7 +2765,7 @@ dependencies = [
[[package]]
name = "lemmy_routes"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-web",
@ -2777,7 +2789,7 @@ dependencies = [
[[package]]
name = "lemmy_server"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"activitypub_federation",
"actix-cors",
@ -2801,6 +2813,7 @@ dependencies = [
"opentelemetry 0.19.0",
"opentelemetry-otlp 0.12.0",
"pict-rs",
"pretty_assertions",
"prometheus",
"reqwest",
"reqwest-middleware",
@ -2819,7 +2832,7 @@ dependencies = [
[[package]]
name = "lemmy_utils"
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
dependencies = [
"actix-web",
"anyhow",
@ -2836,6 +2849,7 @@ dependencies = [
"once_cell",
"openssl",
"percent-encoding",
"pretty_assertions",
"regex",
"reqwest",
"reqwest-middleware",
@ -3127,7 +3141,7 @@ checksum = "ddece26afd34c31585c74a4db0630c376df271c285d682d1e55012197830b6df"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -3212,9 +3226,9 @@ dependencies = [
[[package]]
name = "moka"
version = "0.12.1"
version = "0.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8017ec3548ffe7d4cef7ac0e12b044c01164a74c0f3119420faeaf13490ad8b"
checksum = "f353abec74660d4b8533c2516c86eb062f1ec8ca49a2758f4f2b1b60b06b0c6e"
dependencies = [
"async-lock",
"async-trait",
@ -3367,9 +3381,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
[[package]]
name = "openssl"
version = "0.10.61"
version = "0.10.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b8419dc8cc6d866deb801274bba2e6f8f6108c1bb7fcc10ee5ab864931dbb45"
checksum = "8cde4d2d9200ad5909f8dac647e29482e07c3a35de8a13fce7c9c7747ad9f671"
dependencies = [
"bitflags 2.4.1",
"cfg-if",
@ -3388,7 +3402,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -3399,9 +3413,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
[[package]]
name = "openssl-sys"
version = "0.9.97"
version = "0.9.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3eaad34cdd97d81de97964fc7f29e2d104f483840d906ef56daa1912338460b"
checksum = "c1665caf8ab2dc9aef43d1c0023bd904633a6a05cb30b0ad59bec2ae986e57a7"
dependencies = [
"cc",
"libc",
@ -3814,7 +3828,7 @@ checksum = "4359fd9c9171ec6e8c62926d6faaf553a8dc3f64e1507e76da7911b4f6a04405"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -3942,11 +3956,21 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
[[package]]
name = "pretty_assertions"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.70"
version = "1.0.75"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b"
checksum = "907a61bd0f64c2f29cd1cf1dc34d05176426a3f504a78010f08416ddb7b13708"
dependencies = [
"unicode-ident",
]
@ -4024,7 +4048,7 @@ dependencies = [
"itertools 0.11.0",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -4109,9 +4133,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef"
dependencies = [
"proc-macro2",
]
@ -4169,7 +4193,7 @@ checksum = "b8f439da1766942fe069954da6058b2e6c1760eb878bae76f5be9fc29f56f574"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -4233,7 +4257,7 @@ dependencies = [
"quote",
"refinery-core",
"regex",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -4288,9 +4312,9 @@ checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f"
[[package]]
name = "reqwest"
version = "0.11.22"
version = "0.11.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b"
checksum = "37b1ae8d9ac08420c66222fb9096fc5de435c3c48542bc5336c51892cffafb41"
dependencies = [
"async-compression",
"base64 0.21.5",
@ -4670,9 +4694,9 @@ dependencies = [
[[package]]
name = "serde"
version = "1.0.193"
version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89"
checksum = "0b114498256798c94a0689e1a15fec6005dee8ac1f41de56404b67afc2a4b773"
dependencies = [
"serde_derive",
]
@ -4688,20 +4712,20 @@ dependencies = [
[[package]]
name = "serde_derive"
version = "1.0.193"
version = "1.0.194"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3"
checksum = "a3385e45322e8f9931410f01b3031ec534c3947d0e94c18049af4d9f9907d4e0"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
name = "serde_json"
version = "1.0.108"
version = "1.0.111"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b"
checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4"
dependencies = [
"indexmap 2.1.0",
"itoa",
@ -4765,7 +4789,7 @@ dependencies = [
"darling 0.20.3",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -4790,7 +4814,7 @@ checksum = "91d129178576168c589c9ec973feedf7d3126c01ac2bf08795109aa35b69fb8f"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -4933,7 +4957,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -5065,7 +5089,7 @@ dependencies = [
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -5087,9 +5111,9 @@ dependencies = [
[[package]]
name = "syn"
version = "2.0.40"
version = "2.0.48"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13fa70a4ee923979ffb522cacce59d34421ebdea5625e1073c4326ef9d2dd42e"
checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f"
dependencies = [
"proc-macro2",
"quote",
@ -5194,22 +5218,22 @@ dependencies = [
[[package]]
name = "thiserror"
version = "1.0.50"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2"
checksum = "d54378c645627613241d077a3a79db965db602882668f9136ac42af9ecb730ad"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.50"
version = "1.0.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8"
checksum = "fa0faa943b50f3db30a20aa7e265dbc66076993efed8463e8de414e5d06d3471"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -5274,9 +5298,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]]
name = "tokio"
version = "1.35.0"
version = "1.35.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841d45b238a16291a4e1584e61820b8ae57d696cc5015c459c229ccc6990cc1c"
checksum = "c89b4efa943be685f629b149f53829423f8f5531ea21249408e8e2f8671ec104"
dependencies = [
"backtrace",
"bytes",
@ -5310,7 +5334,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -5647,7 +5671,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -5810,7 +5834,7 @@ dependencies = [
"Inflector",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
"termcolor",
]
@ -5831,7 +5855,7 @@ checksum = "982ee4197351b5c9782847ef5ec1fdcaf50503fb19d68f9771adae314e72b492"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -6011,7 +6035,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
"wasm-bindgen-shared",
]
@ -6045,7 +6069,7 @@ checksum = "f0eb82fcb7930ae6219a7ecfd55b217f5f0893484b7a13022ebb2b2bf20b5283"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
@ -6443,6 +6467,12 @@ dependencies = [
"linked-hash-map",
]
[[package]]
name = "yansi"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec"
[[package]]
name = "zerocopy"
version = "0.7.30"
@ -6460,7 +6490,7 @@ checksum = "be912bf68235a88fbefd1b73415cb218405958d1655b2ece9035a19920bdf6ba"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.40",
"syn 2.0.48",
]
[[package]]
@ -6471,20 +6501,19 @@ checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d"
[[package]]
name = "zstd"
version = "0.12.4"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c"
checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110"
dependencies = [
"zstd-safe",
]
[[package]]
name = "zstd-safe"
version = "6.0.6"
version = "7.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581"
checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e"
dependencies = [
"libc",
"zstd-sys",
]

@ -1,5 +1,6 @@
[workspace.package]
version = "0.19.1-rc.2"
version = "0.19.2-rc.2"
publish = false
edition = "2021"
description = "A link aggregator for the fediverse"
license = "AGPL-3.0"
@ -84,17 +85,17 @@ unused_self = "deny"
unwrap_used = "deny"
[workspace.dependencies]
lemmy_api = { version = "=0.19.1-rc.2", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.1-rc.2", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.1-rc.2", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.1-rc.2", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.1-rc.2", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.1-rc.2", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.1-rc.2", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.1-rc.2", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.1-rc.2", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.1-rc.2", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.0-beta.7", default-features = false, features = [
lemmy_api = { version = "=0.19.2-rc.2", path = "./crates/api" }
lemmy_api_crud = { version = "=0.19.2-rc.2", path = "./crates/api_crud" }
lemmy_apub = { version = "=0.19.2-rc.2", path = "./crates/apub" }
lemmy_utils = { version = "=0.19.2-rc.2", path = "./crates/utils" }
lemmy_db_schema = { version = "=0.19.2-rc.2", path = "./crates/db_schema" }
lemmy_api_common = { version = "=0.19.2-rc.2", path = "./crates/api_common" }
lemmy_routes = { version = "=0.19.2-rc.2", path = "./crates/routes" }
lemmy_db_views = { version = "=0.19.2-rc.2", path = "./crates/db_views" }
lemmy_db_views_actor = { version = "=0.19.2-rc.2", path = "./crates/db_views_actor" }
lemmy_db_views_moderator = { version = "=0.19.2-rc.2", path = "./crates/db_views_moderator" }
activitypub_federation = { version = "0.5.0", default-features = false, features = [
"actix-web",
] }
diesel = "2.1.4"
@ -155,6 +156,7 @@ tokio-postgres = "0.7.10"
tokio-postgres-rustls = "0.10.0"
enum-map = "2.7"
moka = { version = "0.12.1", features = ["future"] }
pretty_assertions = "1.4.0"
[dependencies]
lemmy_api = { workspace = true }
@ -164,7 +166,7 @@ lemmy_utils = { workspace = true }
lemmy_db_schema = { workspace = true }
lemmy_api_common = { workspace = true }
lemmy_routes = { workspace = true }
lemmy_federate = { version = "0.19.1-rc.2", path = "crates/federate" }
lemmy_federate = { version = "0.19.2-rc.2", path = "crates/federate" }
activitypub_federation = { workspace = true }
diesel = { workspace = true }
diesel-async = { workspace = true }
@ -193,3 +195,6 @@ prometheus = { version = "0.13.3", features = ["process"] }
serial_test = { workspace = true }
clap = { version = "4.4.11", features = ["derive"] }
actix-web-prom = "0.7.0"
[dev-dependencies]
pretty_assertions = { workspace = true }

@ -31,6 +31,7 @@ import {
searchPostLocal,
resolveBetaCommunity,
longDelay,
delay,
} from "./shared";
import { EditSite } from "lemmy-js-client";
@ -377,7 +378,9 @@ test("User blocks instance, communities are hidden", async () => {
test("Community follower count is federated", async () => {
// Follow the beta community from alpha
let resolved = await resolveBetaCommunity(alpha);
let community = await createCommunity(beta);
let community_id = community.community_view.community.actor_id;
let resolved = await resolveCommunity(alpha, community_id);
if (!resolved.community) {
throw "Missing beta community";
}
@ -385,7 +388,7 @@ test("Community follower count is federated", async () => {
await followCommunity(alpha, true, resolved.community.community.id);
let followed = (
await waitUntil(
() => resolveBetaCommunity(alpha),
() => resolveCommunity(alpha, community_id),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -394,7 +397,7 @@ test("Community follower count is federated", async () => {
expect(followed?.counts.subscribers).toBe(1);
// Follow the community from gamma
resolved = await resolveBetaCommunity(gamma);
resolved = await resolveCommunity(gamma, community_id);
if (!resolved.community) {
throw "Missing beta community";
}
@ -402,7 +405,7 @@ test("Community follower count is federated", async () => {
await followCommunity(gamma, true, resolved.community.community.id);
followed = (
await waitUntil(
() => resolveBetaCommunity(gamma),
() => resolveCommunity(gamma, community_id),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -411,7 +414,7 @@ test("Community follower count is federated", async () => {
expect(followed?.counts?.subscribers).toBe(2);
// Follow the community from delta
resolved = await resolveBetaCommunity(delta);
resolved = await resolveCommunity(delta, community_id);
if (!resolved.community) {
throw "Missing beta community";
}
@ -419,7 +422,7 @@ test("Community follower count is federated", async () => {
await followCommunity(delta, true, resolved.community.community.id);
followed = (
await waitUntil(
() => resolveBetaCommunity(delta),
() => resolveCommunity(delta, community_id),
c => c.community?.subscribed === "Subscribed",
)
).community;
@ -480,3 +483,31 @@ test("Dont receive community activities after unsubscribe", async () => {
let postResBeta = searchPostLocal(beta, postRes.post_view.post);
expect((await postResBeta).posts.length).toBe(0);
});
test("Fetch community, includes posts", async () => {
let communityRes = await createCommunity(alpha);
expect(communityRes.community_view.community.name).toBeDefined();
expect(communityRes.community_view.counts.subscribers).toBe(1);
let postRes = await createPost(
alpha,
communityRes.community_view.community.id,
);
expect(postRes.post_view.post).toBeDefined();
let resolvedCommunity = await waitUntil(
() =>
resolveCommunity(beta, communityRes.community_view.community.actor_id),
c => c.community?.community.id != undefined,
);
let betaCommunity = resolvedCommunity.community;
expect(betaCommunity?.community.actor_id).toBe(
communityRes.community_view.community.actor_id,
);
await longDelay();
let post_listing = await getPosts(beta, "All", betaCommunity?.community.id);
expect(post_listing.posts.length).toBe(1);
expect(post_listing.posts[0].post.ap_id).toBe(postRes.post_view.post.ap_id);
});

@ -805,10 +805,12 @@ export async function listCommentReports(
export function getPosts(
api: LemmyHttp,
listingType?: ListingType,
community_id?: number,
): Promise<GetPostsResponse> {
let form: GetPosts = {
type_: listingType,
limit: 50,
community_id,
};
return api.getPosts(form);
}

@ -112,18 +112,19 @@ test("Delete user", async () => {
).toBe(true);
});
test("Requests with invalid auth should be treated as unauthenticated", async () => {
test("Requests with invalid auth should throw error", async () => {
let invalid_auth = new LemmyHttp(alphaUrl, {
headers: { Authorization: "Bearer foobar" },
fetchFunction,
});
let site = await getSite(invalid_auth);
expect(site.my_user).toBeUndefined();
expect(site.site_view).toBeDefined();
await expect(getSite(invalid_auth)).rejects.toStrictEqual(
Error("incorrect_login"),
);
let form: GetPosts = {};
let posts = invalid_auth.getPosts(form);
expect((await posts).posts).toBeDefined();
await expect(invalid_auth.getPosts(form)).rejects.toStrictEqual(
Error("incorrect_login"),
);
});
test("Create user with Arabic name", async () => {

@ -34,7 +34,7 @@
# Name of the postgres database for lemmy
database: "string"
# Maximum number of active sql connections
pool_size: 95
pool_size: 30
}
# Settings related to activitypub federation
# Pictrs image server configuration.

@ -1,5 +1,6 @@
[package]
name = "lemmy_api"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true
@ -41,3 +42,4 @@ actix-web-httpauth = "0.8.1"
serial_test = { workspace = true }
tokio = { workspace = true }
elementtree = "1.2.3"
pretty_assertions = { workspace = true }

@ -0,0 +1,24 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
comment::{ListCommentLikes, ListCommentLikesResponse},
context::LemmyContext,
utils::is_admin,
};
use lemmy_db_views::structs::{LocalUserView, VoteView};
use lemmy_utils::error::LemmyError;
/// Lists likes for a comment
#[tracing::instrument(skip(context))]
pub async fn list_comment_likes(
data: Query<ListCommentLikes>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListCommentLikesResponse>, LemmyError> {
// Make sure user is an admin
is_admin(&local_user_view)?;
let comment_likes =
VoteView::list_for_comment(&mut context.pool(), data.comment_id, data.page, data.limit).await?;
Ok(Json(ListCommentLikesResponse { comment_likes }))
}

@ -1,3 +1,4 @@
pub mod distinguish;
pub mod like;
pub mod list_comment_likes;
pub mod save;

@ -23,7 +23,7 @@ pub async fn resolve_comment_report(
check_community_mod_action(
&local_user_view.person,
report.community.id,
false,
true,
&mut context.pool(),
)
.await?;

@ -2,15 +2,11 @@ use actix_web::{http::header::Header, HttpRequest};
use actix_web_httpauth::headers::authorization::{Authorization, Bearer};
use base64::{engine::general_purpose::STANDARD_NO_PAD as base64, Engine};
use captcha::Captcha;
use lemmy_api_common::{
claims::Claims,
context::LemmyContext,
utils::{check_user_valid, local_site_to_slur_regex, AUTH_COOKIE_NAME},
};
use lemmy_api_common::utils::{local_site_to_slur_regex, AUTH_COOKIE_NAME};
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::{
error::{LemmyError, LemmyErrorExt, LemmyErrorExt2, LemmyErrorType, LemmyResult},
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
utils::slurs::check_slurs,
};
use std::io::Cursor;
@ -141,20 +137,6 @@ pub(crate) fn build_totp_2fa(
.with_lemmy_type(LemmyErrorType::CouldntGenerateTotp)
}
#[tracing::instrument(skip_all)]
pub async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;
Ok(local_user_view)
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]

@ -1,10 +1,10 @@
use crate::{local_user_view_from_jwt, read_auth_token};
use crate::read_auth_token;
use actix_web::{
web::{Data, Json},
HttpRequest,
};
use lemmy_api_common::{context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorType};
use lemmy_api_common::{claims::Claims, context::LemmyContext, SuccessResponse};
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
/// Returns an error message if the auth token is invalid for any reason. Necessary because other
/// endpoints silently treat any call with invalid auth as unauthenticated.
@ -15,7 +15,9 @@ pub async fn validate_auth(
) -> Result<Json<SuccessResponse>, LemmyError> {
let jwt = read_auth_token(&req)?;
if let Some(jwt) = jwt {
local_user_view_from_jwt(&jwt, &context).await?;
Claims::validate(&jwt, &context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
} else {
Err(LemmyErrorType::NotLoggedIn)?;
}

@ -0,0 +1,24 @@
use actix_web::web::{Data, Json, Query};
use lemmy_api_common::{
context::LemmyContext,
post::{ListPostLikes, ListPostLikesResponse},
utils::is_admin,
};
use lemmy_db_views::structs::{LocalUserView, VoteView};
use lemmy_utils::error::LemmyError;
/// Lists likes for a post
#[tracing::instrument(skip(context))]
pub async fn list_post_likes(
data: Query<ListPostLikes>,
context: Data<LemmyContext>,
local_user_view: LocalUserView,
) -> Result<Json<ListPostLikesResponse>, LemmyError> {
// Make sure user is an admin
is_admin(&local_user_view)?;
let post_likes =
VoteView::list_for_post(&mut context.pool(), data.post_id, data.page, data.limit).await?;
Ok(Json(ListPostLikesResponse { post_likes }))
}

@ -1,6 +1,7 @@
pub mod feature;
pub mod get_link_metadata;
pub mod like;
pub mod list_post_likes;
pub mod lock;
pub mod mark_read;
pub mod save;

@ -23,7 +23,7 @@ pub async fn resolve_post_report(
check_community_mod_action(
&local_user_view.person,
report.community.id,
false,
true,
&mut context.pool(),
)
.await?;

@ -49,6 +49,7 @@ pub(crate) mod tests {
use chrono::{DateTime, NaiveDate, Utc};
use elementtree::Element;
use lemmy_db_schema::newtypes::DbUrl;
use pretty_assertions::assert_eq;
use url::Url;
#[tokio::test]

@ -77,3 +77,4 @@ ignored = ["getrandom"]
[dev-dependencies]
serial_test = { workspace = true }
reqwest-middleware = { workspace = true }
pretty_assertions = { workspace = true }

@ -6,7 +6,7 @@ use lemmy_db_schema::{
newtypes::LocalUserId,
source::login_token::{LoginToken, LoginTokenCreateForm},
};
use lemmy_utils::error::{LemmyErrorExt, LemmyErrorType, LemmyResult};
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize)]
@ -25,8 +25,7 @@ impl Claims {
validation.required_spec_claims.remove("exp");
let jwt_secret = &context.secret().jwt_secret;
let key = DecodingKey::from_secret(jwt_secret.as_ref());
let claims =
decode::<Claims>(jwt, &key, &validation).with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let claims = decode::<Claims>(jwt, &key, &validation)?;
let user_id = LocalUserId(claims.claims.sub.parse()?);
let is_valid = LoginToken::validate(&mut context.pool(), user_id, jwt).await?;
if !is_valid {
@ -89,6 +88,7 @@ mod tests {
utils::build_db_pool_for_tests,
};
use lemmy_utils::rate_limit::RateLimitCell;
use pretty_assertions::assert_eq;
use reqwest::Client;
use reqwest_middleware::ClientBuilder;
use serial_test::serial;

@ -3,7 +3,7 @@ use lemmy_db_schema::{
CommentSortType,
ListingType,
};
use lemmy_db_views::structs::{CommentReportView, CommentView};
use lemmy_db_views::structs::{CommentReportView, CommentView, VoteView};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
#[cfg(feature = "full")]
@ -176,3 +176,22 @@ pub struct ListCommentReports {
pub struct ListCommentReportsResponse {
pub comment_reports: Vec<CommentReportView>,
}
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
/// List comment likes. Admins-only.
pub struct ListCommentLikes {
pub comment_id: CommentId,
pub page: Option<i64>,
pub limit: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
/// The comment likes response
pub struct ListCommentLikesResponse {
pub comment_likes: Vec<VoteView>,
}

@ -43,5 +43,5 @@ impl Default for SuccessResponse {
/// how long to sleep based on how many retries have already happened
pub fn federate_retry_sleep_duration(retry_count: i32) -> Duration {
Duration::from_secs_f64(10.0 * 2.0_f64.powf(f64::from(retry_count)))
Duration::from_secs_f64(2.0_f64.powf(f64::from(retry_count)))
}

@ -4,7 +4,7 @@ use lemmy_db_schema::{
PostFeatureType,
SortType,
};
use lemmy_db_views::structs::{PaginationCursor, PostReportView, PostView};
use lemmy_db_views::structs::{PaginationCursor, PostReportView, PostView, VoteView};
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
use serde::{Deserialize, Serialize};
use serde_with::skip_serializing_none;
@ -252,3 +252,22 @@ pub struct SiteMetadata {
pub(crate) image: Option<DbUrl>,
pub embed_video_url: Option<DbUrl>,
}
#[skip_serializing_none]
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
/// List post likes. Admins-only.
pub struct ListPostLikes {
pub post_id: PostId,
pub page: Option<i64>,
pub limit: Option<i64>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS))]
#[cfg_attr(feature = "full", ts(export))]
/// The post likes response
pub struct ListPostLikesResponse {
pub post_likes: Vec<VoteView>,
}

@ -309,6 +309,7 @@ mod tests {
use crate::request::{client_builder, fetch_site_metadata, html_to_site_metadata, SiteMetadata};
use lemmy_utils::settings::SETTINGS;
use pretty_assertions::assert_eq;
use url::Url;
// These helped with testing

@ -842,6 +842,7 @@ mod tests {
use crate::utils::{honeypot_check, limit_expire_time, password_length_check};
use chrono::{Days, Utc};
use pretty_assertions::assert_eq;
#[test]
#[rustfmt::skip]

@ -1,5 +1,6 @@
[package]
name = "lemmy_api_crud"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true

@ -1,5 +1,6 @@
[package]
name = "lemmy_apub"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true
@ -52,3 +53,4 @@ serial_test = { workspace = true }
reqwest-middleware = { workspace = true }
task-local-extensions = "0.1.4"
assert-json-diff = "2.0.2"
pretty_assertions = { workspace = true }

@ -1,7 +1,7 @@
use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
insert_received_activity,
objects::{community::ApubCommunity, person::ApubPerson},
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
protocol::{activities::community::report::Report, InCommunity},
PostOrComment,
};
@ -19,8 +19,9 @@ use lemmy_db_schema::{
community::Community,
person::Person,
post_report::{PostReport, PostReportForm},
site::Site,
},
traits::Reportable,
traits::{Crud, Reportable},
};
use lemmy_utils::error::LemmyError;
use url::Url;
@ -44,18 +45,31 @@ impl Report {
let report = Report {
actor: actor.id().into(),
to: [community.id().into()],
object: object_id,
object: object_id.clone(),
summary: reason,
kind,
id: id.clone(),
audience: Some(community.id().into()),
};
let inbox = if community.local {
ActivitySendTargets::empty()
} else {
ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox())
// send report to the community where object was posted
let mut inboxes = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
// also send report to user's home instance if possible
let object_creator_id = match object_id.dereference_local(&context).await? {
PostOrComment::Post(p) => p.creator_id,
PostOrComment::Comment(c) => c.creator_id,
};
send_lemmy_activity(&context, report, &actor, inbox, false).await
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?
.map(Into::into);
if let Some(inbox) = object_creator_site.map(|s| s.shared_inbox_or_inbox()) {
inboxes.add_inbox(inbox);
}
send_lemmy_activity(&context, report, &actor, inboxes, false).await
}
}

@ -316,6 +316,7 @@ mod tests {
use lemmy_db_views::structs::LocalUserView;
use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
use pretty_assertions::assert_eq;
use serial_test::serial;
use std::time::Duration;
use tokio::time::sleep;
@ -403,7 +404,7 @@ mod tests {
let mut backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
for _ in 0..251 {
for _ in 0..2501 {
backup
.followed_communities
.push("http://example.com".parse()?);

@ -123,6 +123,7 @@ mod tests {
traits::Crud,
};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -96,11 +96,15 @@ impl Collection for ApubCommunityOutbox {
// process items in parallel, to avoid long delay from fetch_site_metadata() and other processing
join_all(outbox_activities.into_iter().map(|activity| {
async {
// use separate request counter for each item, otherwise there will be problems with
// parallel processing
let verify = activity.verify(data).await;
if verify.is_ok() {
activity.receive(data).await.ok();
// Receiving announce requires at least one local community follower for anti spam purposes.
// This won't be the case for newly fetched communities, so we extract the inner activity
// and handle it directly to bypass this check.
let inner = activity.object.object(data).await.map(TryInto::try_into);
if let Ok(Ok(AnnouncableActivities::CreateOrUpdatePost(inner))) = inner {
let verify = inner.verify(data).await;
if verify.is_ok() {
inner.receive(data).await.ok();
}
}
}
}))

@ -198,6 +198,7 @@ pub(crate) mod tests {
use html2md::parse_html;
use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
async fn prepare_comment_test(

@ -28,9 +28,8 @@ use lemmy_db_schema::{
traits::{ApubActor, Crud},
};
use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::{error::LemmyError, utils::markdown::markdown_to_html};
use lemmy_utils::{error::LemmyError, spawn_try_task, utils::markdown::markdown_to_html};
use std::ops::Deref;
use tracing::debug;
use url::Url;
#[derive(Clone, Debug)]
@ -142,21 +141,16 @@ impl Object for ApubCommunity {
// Fetching mods and outbox is not necessary for Lemmy to work, so ignore errors. Besides,
// we need to ignore these errors so that tests can work entirely offline.
let fetch_outbox = group.outbox.dereference(&community, context);
let fetch_followers = group.followers.dereference(&community, context);
if let Some(moderators) = group.attributed_to {
let fetch_moderators = moderators.dereference(&community, context);
// Fetch mods, outbox and followers in parallel
let res = tokio::join!(fetch_outbox, fetch_moderators, fetch_followers);
res.0.map_err(|e| debug!("{}", e)).ok();
res.1.map_err(|e| debug!("{}", e)).ok();
res.2.map_err(|e| debug!("{}", e)).ok();
} else {
let res = tokio::join!(fetch_outbox, fetch_followers);
res.0.map_err(|e| debug!("{}", e)).ok();
res.1.map_err(|e| debug!("{}", e)).ok();
}
let community_ = community.clone();
let context_ = context.reset_request_count();
spawn_try_task(async move {
group.outbox.dereference(&community_, &context_).await?;
group.followers.dereference(&community_, &context_).await?;
if let Some(moderators) = group.attributed_to {
moderators.dereference(&community_, &context_).await?;
}
Ok(())
});
Ok(community)
}
@ -224,6 +218,7 @@ pub(crate) mod tests {
use activitypub_federation::fetch::collection_id::CollectionId;
use lemmy_db_schema::{source::site::Site, traits::Crud};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
pub(crate) async fn parse_lemmy_community(
@ -240,8 +235,6 @@ pub(crate) mod tests {
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward")?;
ApubCommunity::verify(&json, &url, &context2).await?;
let community = ApubCommunity::from_json(json, &context2).await?;
// this makes requests to the (intentionally broken) outbox and followers collections
assert_eq!(context2.request_count(), 2);
Ok(community)
}

@ -208,6 +208,7 @@ pub(crate) mod tests {
use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
use lemmy_db_schema::traits::Crud;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> LemmyResult<ApubSite> {

@ -219,6 +219,7 @@ pub(crate) mod tests {
use activitypub_federation::fetch::object_id::ObjectId;
use lemmy_db_schema::{source::site::Site, traits::Crud};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
pub(crate) async fn parse_lemmy_person(

@ -305,6 +305,7 @@ mod tests {
};
use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -153,6 +153,7 @@ mod tests {
use assert_json_diff::assert_json_include;
use lemmy_db_schema::source::site::Site;
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
use serial_test::serial;
async fn prepare_comment_test(

@ -17,6 +17,7 @@ mod tests {
tests::{test_json, test_parse_lemmy_item},
};
use lemmy_utils::error::LemmyResult;
use pretty_assertions::assert_eq;
#[test]
fn test_parse_lemmy_collections() -> LemmyResult<()> {

@ -79,6 +79,7 @@ uuid = { workspace = true, features = ["v4"] }
[dev-dependencies]
serial_test = { workspace = true }
pretty_assertions = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["strum"]

@ -49,6 +49,7 @@ mod tests {
traits::{Crud, Likeable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -47,6 +47,7 @@ mod tests {
traits::{Crud, Followable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -34,6 +34,7 @@ mod tests {
traits::{Crud, Likeable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -68,6 +68,7 @@ mod tests {
traits::{Crud, Likeable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -31,6 +31,7 @@ mod tests {
traits::Crud,
utils::{build_db_pool_for_tests, DbPool},
};
use pretty_assertions::assert_eq;
use serial_test::serial;
async fn prepare_site_with_community(

@ -67,6 +67,7 @@ mod tests {
use super::*;
use crate::{source::activity::ActorType, utils::build_db_pool_for_tests};
use pretty_assertions::assert_eq;
use serde_json::json;
use serial_test::serial;
use url::Url;

@ -412,6 +412,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
async fn test_langs1(pool: &mut DbPool<'_>) -> Vec<LanguageId> {

@ -263,6 +263,7 @@ mod tests {
utils::build_db_pool_for_tests,
};
use diesel_ltree::Ltree;
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -89,6 +89,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -366,6 +366,7 @@ mod tests {
traits::{Bannable, Crud, Followable, Joinable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -56,6 +56,7 @@ mod tests {
source::{federation_allowlist::FederationAllowList, instance::Instance},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -46,6 +46,7 @@ mod tests {
#![allow(clippy::indexing_slicing)]
use crate::{source::language::Language, utils::build_db_pool_for_tests};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -500,6 +500,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -95,6 +95,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -191,6 +191,7 @@ mod tests {
traits::{Crud, Followable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -90,6 +90,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -366,6 +366,7 @@ mod tests {
traits::{Crud, Likeable, Saveable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
use std::collections::HashSet;

@ -75,6 +75,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
async fn init(pool: &mut DbPool<'_>) -> (Person, PostReport) {

@ -87,6 +87,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -216,3 +216,12 @@ pub enum PostFeatureType {
/// Features to the top of the community.
Community,
}
/// Wrapper for assert_eq! macro. Checks that vec matches the given length, and prints the
/// vec on failure.
#[macro_export]
macro_rules! assert_length {
($len:expr, $vec:expr) => {{
assert_eq!($len, $vec.len(), "Vec has wrong length: {:?}", $vec)
}};
}

@ -42,7 +42,7 @@ use rustls::{
use std::{
ops::{Deref, DerefMut},
sync::Arc,
time::{Duration, SystemTime},
time::SystemTime,
};
use tracing::{error, info};
use url::Url;
@ -51,7 +51,6 @@ const FETCH_LIMIT_DEFAULT: i64 = 10;
pub const FETCH_LIMIT_MAX: i64 = 50;
pub const SITEMAP_LIMIT: i64 = 50000;
pub const SITEMAP_DAYS: i64 = 31;
const POOL_TIMEOUT: Option<Duration> = Some(Duration::from_secs(5));
pub const RANK_DEFAULT: f64 = 0.0001;
pub type ActualDbPool = Pool<AsyncPgConnection>;
@ -302,9 +301,6 @@ pub async fn build_db_pool() -> Result<ActualDbPool, LemmyError> {
};
let pool = Pool::builder(manager)
.max_size(SETTINGS.database.pool_size)
.wait_timeout(POOL_TIMEOUT)
.create_timeout(POOL_TIMEOUT)
.recycle_timeout(POOL_TIMEOUT)
.runtime(Runtime::Tokio1)
.build()?;
@ -468,6 +464,7 @@ mod tests {
use super::{fuzzy_search, *};
use crate::utils::is_email_regex;
use pretty_assertions::assert_eq;
#[test]
fn test_fuzzy_search() {

@ -42,3 +42,4 @@ actix-web = { workspace = true, optional = true }
serial_test = { workspace = true }
tokio = { workspace = true }
chrono = { workspace = true }
pretty_assertions = { workspace = true }

@ -234,6 +234,7 @@ mod tests {
traits::{Crud, Joinable, Reportable},
utils::{build_db_pool_for_tests, RANK_DEFAULT},
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -399,6 +399,7 @@ mod tests {
};
use lemmy_db_schema::{
aggregates::structs::CommentAggregates,
assert_length,
impls::actor_language::UNDETERMINED_ID,
newtypes::LanguageId,
source::{
@ -416,6 +417,7 @@ mod tests {
utils::{build_db_pool_for_tests, RANK_DEFAULT},
SubscribedType,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
struct Data {
@ -636,7 +638,7 @@ mod tests {
);
// Make sure its 1, not showing the blocked comment
assert_eq!(5, read_comment_views_with_person.len());
assert_length!(5, read_comment_views_with_person);
let read_comment_from_blocked_person = CommentView::read(
pool,
@ -663,7 +665,7 @@ mod tests {
read_liked_comment_views[0]
);
assert_eq!(1, read_liked_comment_views.len());
assert_length!(1, read_liked_comment_views);
let read_disliked_comment_views: Vec<CommentView> = CommentQuery {
local_user: (Some(&data.timmy_local_user_view)),
@ -707,8 +709,8 @@ mod tests {
.unwrap();
// Make sure the comment parent-limited fetch is correct
assert_eq!(6, read_comment_views_top_path.len());
assert_eq!(4, read_comment_views_child_path.len());
assert_length!(6, read_comment_views_top_path);
assert_length!(4, read_comment_views_child_path);
// Make sure it contains the parent, but not the comment from the other tree
let child_comments = read_comment_views_child_path
@ -732,7 +734,7 @@ mod tests {
expected_comment_view(&data, pool).await,
read_comment_views_top_max_depth[0]
);
assert_eq!(1, read_comment_views_top_max_depth.len());
assert_length!(1, read_comment_views_top_max_depth);
let child_path = data.inserted_comment_1.path.clone();
let read_comment_views_parent_max_depth = CommentQuery {
@ -751,7 +753,7 @@ mod tests {
.comment
.content
.eq("Comment 3"));
assert_eq!(3, read_comment_views_parent_max_depth.len());
assert_length!(3, read_comment_views_parent_max_depth);
cleanup(data, pool).await;
}
@ -772,7 +774,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(5, all_languages.len());
assert_length!(5, all_languages);
// change user lang to finnish, should only show one post in finnish and one undetermined
let finnish_id = Language::read_id_from_code(pool, Some("fi"))
@ -793,7 +795,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(2, finnish_comments.len());
assert_length!(2, finnish_comments);
let finnish_comment = finnish_comments
.iter()
.find(|c| c.comment.language_id == finnish_id);
@ -818,7 +820,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, undetermined_comment.len());
assert_length!(1, undetermined_comment);
cleanup(data, pool).await;
}

@ -22,3 +22,5 @@ pub mod registration_application_view;
#[cfg(feature = "full")]
pub mod site_view;
pub mod structs;
#[cfg(feature = "full")]
pub mod vote_view;

@ -196,6 +196,7 @@ mod tests {
structs::LocalUserView,
};
use lemmy_db_schema::{
assert_length,
source::{
community::{Community, CommunityInsertForm, CommunityModerator, CommunityModeratorForm},
instance::Instance,
@ -208,6 +209,7 @@ mod tests {
traits::{Crud, Joinable, Reportable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]
@ -382,7 +384,7 @@ mod tests {
.list(pool, &timmy_view)
.await
.unwrap();
assert_eq!(reports_after_resolve.len(), 1);
assert_length!(1, reports_after_resolve);
assert_eq!(reports_after_resolve[0].creator.id, inserted_sara.id);
// Make sure the counts are correct

@ -729,6 +729,7 @@ mod tests {
use chrono::Utc;
use lemmy_db_schema::{
aggregates::structs::PostAggregates,
assert_length,
impls::actor_language::UNDETERMINED_ID,
newtypes::LanguageId,
source::{
@ -749,6 +750,7 @@ mod tests {
SortType,
SubscribedType,
};
use pretty_assertions::{assert_eq, assert_ne};
use serial_test::serial;
use std::{collections::HashSet, time::Duration};
@ -914,7 +916,7 @@ mod tests {
let mut expected_post_listing_with_user = expected_post_view(&data, pool).await;
// Should be only one person, IE the bot post, and blocked should be missing
assert_eq!(1, read_post_listing.len());
assert_length!(1, read_post_listing);
assert_eq!(expected_post_listing_with_user, read_post_listing[0]);
expected_post_listing_with_user.my_vote = None;
@ -943,7 +945,7 @@ mod tests {
.await
.unwrap();
// should include bot post which has "undetermined" language
assert_eq!(2, post_listings_with_bots.len());
assert_length!(2, post_listings_with_bots);
cleanup(data, pool).await;
}
@ -972,7 +974,7 @@ mod tests {
let expected_post_listing_no_person = expected_post_view(&data, pool).await;
// Should be 2 posts, with the bot post, and the blocked
assert_eq!(3, read_post_listing_multiple_no_person.len());
assert_length!(3, read_post_listing_multiple_no_person);
assert_eq!(
expected_post_listing_no_person,
@ -1009,7 +1011,7 @@ mod tests {
.await
.unwrap();
// Should be 0 posts after the community block
assert_eq!(0, read_post_listings_with_person_after_block.len());
assert_length!(0, read_post_listings_with_person_after_block);
CommunityBlock::unblock(pool, &community_block)
.await
@ -1074,7 +1076,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, read_post_listing.len());
assert_length!(1, read_post_listing);
assert_eq!(expected_post_with_upvote, read_post_listing[0]);
@ -1199,7 +1201,7 @@ mod tests {
.unwrap();
// no language filters specified, all posts should be returned
assert_eq!(3, post_listings_all.len());
assert_length!(3, post_listings_all);
let french_id = Language::read_id_from_code(pool, Some("fr"))
.await
@ -1219,7 +1221,7 @@ mod tests {
.unwrap();
// only one post in french and one undetermined should be returned
assert_eq!(2, post_listing_french.len());
assert_length!(2, post_listing_french);
assert!(post_listing_french
.iter()
.any(|p| p.post.language_id == french_id));
@ -1241,7 +1243,7 @@ mod tests {
.unwrap();
// french post and undetermined language post should be returned
assert_eq!(2, post_listings_french_und.len());
assert_length!(2, post_listings_french_und);
assert_eq!(
UNDETERMINED_ID,
post_listings_french_und[0].post.language_id
@ -1279,7 +1281,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, post_listings_no_admin.len());
assert_length!(1, post_listings_no_admin);
// Removed bot post is shown to admins on its profile page
data.local_user_view.local_user.admin = true;
@ -1378,7 +1380,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(post_listings_all.len(), 3);
assert_length!(3, post_listings_all);
// block the instance
let block_form = InstanceBlockForm {
@ -1395,7 +1397,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(post_listings_blocked.len(), 2);
assert_length!(2, post_listings_blocked);
assert_ne!(
post_listings_blocked[0].post.id,
post_from_blocked_instance.id
@ -1414,7 +1416,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(post_listings_blocked.len(), 3);
assert_length!(3, post_listings_blocked);
Instance::delete(pool, blocked_instance.id).await.unwrap();
cleanup(data, pool).await;
@ -1534,7 +1536,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, post_listings_hide_read.len());
assert_length!(1, post_listings_hide_read);
cleanup(data, pool).await;
}

@ -112,6 +112,7 @@ mod tests {
use crate::private_message_report_view::PrivateMessageReportQuery;
use lemmy_db_schema::{
assert_length,
source::{
instance::Instance,
person::{Person, PersonInsertForm},
@ -121,6 +122,7 @@ mod tests {
traits::{Crud, Reportable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]
@ -170,7 +172,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, reports.len());
assert_length!(1, reports);
assert!(!reports[0].private_message_report.resolved);
assert_eq!(inserted_timmy.name, reports[0].private_message_creator.name);
assert_eq!(inserted_jessica.name, reports[0].creator.name);
@ -196,7 +198,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(1, reports.len());
assert_length!(1, reports);
assert!(reports[0].private_message_report.resolved);
assert!(reports[0].resolver.is_some());
assert_eq!(

@ -159,6 +159,7 @@ mod tests {
use crate::{private_message_view::PrivateMessageQuery, structs::PrivateMessageView};
use lemmy_db_schema::{
assert_length,
source::{
instance::Instance,
person::{Person, PersonInsertForm},
@ -168,6 +169,7 @@ mod tests {
traits::{Blockable, Crud},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]
@ -250,7 +252,7 @@ mod tests {
.await
.unwrap();
assert_eq!(timmy_messages.len(), 3);
assert_length!(3, &timmy_messages);
assert_eq!(timmy_messages[0].creator.id, jess.id);
assert_eq!(timmy_messages[0].recipient.id, timmy.id);
assert_eq!(timmy_messages[1].creator.id, timmy.id);
@ -267,7 +269,7 @@ mod tests {
.await
.unwrap();
assert_eq!(timmy_unread_messages.len(), 2);
assert_length!(2, &timmy_unread_messages);
assert_eq!(timmy_unread_messages[0].creator.id, jess.id);
assert_eq!(timmy_unread_messages[0].recipient.id, timmy.id);
assert_eq!(timmy_unread_messages[1].creator.id, sara.id);
@ -282,7 +284,7 @@ mod tests {
.await
.unwrap();
assert_eq!(timmy_sara_messages.len(), 2);
assert_length!(2, &timmy_sara_messages);
assert_eq!(timmy_sara_messages[0].creator.id, timmy.id);
assert_eq!(timmy_sara_messages[0].recipient.id, sara.id);
assert_eq!(timmy_sara_messages[1].creator.id, sara.id);
@ -297,7 +299,7 @@ mod tests {
.await
.unwrap();
assert_eq!(timmy_sara_unread_messages.len(), 1);
assert_length!(1, &timmy_sara_unread_messages);
assert_eq!(timmy_sara_unread_messages[0].creator.id, sara.id);
assert_eq!(timmy_sara_unread_messages[0].recipient.id, timmy.id);
@ -327,7 +329,7 @@ mod tests {
.await
.unwrap();
assert_eq!(timmy_messages.len(), 1);
assert_length!(1, &timmy_messages);
let timmy_unread_messages = PrivateMessageView::get_unread_messages(pool, timmy.id)
.await

@ -149,6 +149,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]

@ -184,3 +184,14 @@ pub struct CustomEmojiView {
pub custom_emoji: CustomEmoji,
pub keywords: Vec<CustomEmojiKeyword>,
}
#[skip_serializing_none]
#[derive(Debug, PartialEq, Eq, Serialize, Deserialize, Clone)]
#[cfg_attr(feature = "full", derive(TS, Queryable))]
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
#[cfg_attr(feature = "full", ts(export))]
/// A vote view for checking a post or comments votes.
pub struct VoteView {
pub creator: Person,
pub score: i16,
}

@ -0,0 +1,196 @@
use crate::structs::VoteView;
use diesel::{result::Error, ExpressionMethods, QueryDsl};
use diesel_async::RunQueryDsl;
use lemmy_db_schema::{
newtypes::{CommentId, PostId},
schema::{comment_like, person, post_like},
utils::{get_conn, limit_and_offset, DbPool},
};
impl VoteView {
pub async fn list_for_post(
pool: &mut DbPool<'_>,
post_id: PostId,
page: Option<i64>,
limit: Option<i64>,
) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
let (limit, offset) = limit_and_offset(page, limit)?;
post_like::table
.inner_join(person::table)
.filter(post_like::post_id.eq(post_id))
.select((person::all_columns, post_like::score))
.order_by(post_like::score)
.limit(limit)
.offset(offset)
.load::<Self>(conn)
.await
}
pub async fn list_for_comment(
pool: &mut DbPool<'_>,
comment_id: CommentId,
page: Option<i64>,
limit: Option<i64>,
) -> Result<Vec<Self>, Error> {
let conn = &mut get_conn(pool).await?;
let (limit, offset) = limit_and_offset(page, limit)?;
comment_like::table
.inner_join(person::table)
.filter(comment_like::comment_id.eq(comment_id))
.select((person::all_columns, comment_like::score))
.order_by(comment_like::score)
.limit(limit)
.offset(offset)
.load::<Self>(conn)
.await
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
#![allow(clippy::indexing_slicing)]
use crate::structs::VoteView;
use lemmy_db_schema::{
source::{
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm},
community::{Community, CommunityInsertForm},
instance::Instance,
person::{Person, PersonInsertForm},
post::{Post, PostInsertForm, PostLike, PostLikeForm},
},
traits::{Crud, Likeable},
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
#[tokio::test]
#[serial]
async fn post_and_comment_vote_views() {
let pool = &build_db_pool_for_tests().await;
let pool = &mut pool.into();
let inserted_instance = Instance::read_or_create(pool, "my_domain.tld".to_string())
.await
.unwrap();
let new_person = PersonInsertForm::builder()
.name("timmy_vv".into())
.public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let inserted_timmy = Person::create(pool, &new_person).await.unwrap();
let new_person_2 = PersonInsertForm::builder()
.name("sara_vv".into())
.public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let inserted_sara = Person::create(pool, &new_person_2).await.unwrap();
let new_community = CommunityInsertForm::builder()
.name("test community vv".to_string())
.title("nada".to_owned())
.public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let inserted_community = Community::create(pool, &new_community).await.unwrap();
let new_post = PostInsertForm::builder()
.name("A test post vv".into())
.creator_id(inserted_timmy.id)
.community_id(inserted_community.id)
.build();
let inserted_post = Post::create(pool, &new_post).await.unwrap();
let comment_form = CommentInsertForm::builder()
.content("A test comment vv".into())
.creator_id(inserted_timmy.id)
.post_id(inserted_post.id)
.build();
let inserted_comment = Comment::create(pool, &comment_form, None).await.unwrap();
// Timmy upvotes his own post
let timmy_post_vote_form = PostLikeForm {
post_id: inserted_post.id,
person_id: inserted_timmy.id,
score: 1,
};
PostLike::like(pool, &timmy_post_vote_form).await.unwrap();
// Sara downvotes timmy's post
let sara_post_vote_form = PostLikeForm {
post_id: inserted_post.id,
person_id: inserted_sara.id,
score: -1,
};
PostLike::like(pool, &sara_post_vote_form).await.unwrap();
let expected_post_vote_views = [
VoteView {
creator: inserted_sara.clone(),
score: -1,
},
VoteView {
creator: inserted_timmy.clone(),
score: 1,
},
];
let read_post_vote_views = VoteView::list_for_post(pool, inserted_post.id, None, None)
.await
.unwrap();
assert_eq!(read_post_vote_views, expected_post_vote_views);
// Timothy votes down his own comment
let timmy_comment_vote_form = CommentLikeForm {
post_id: inserted_post.id,
comment_id: inserted_comment.id,
person_id: inserted_timmy.id,
score: -1,
};
CommentLike::like(pool, &timmy_comment_vote_form)
.await
.unwrap();
// Sara upvotes timmy's comment
let sara_comment_vote_form = CommentLikeForm {
post_id: inserted_post.id,
comment_id: inserted_comment.id,
person_id: inserted_sara.id,
score: 1,
};
CommentLike::like(pool, &sara_comment_vote_form)
.await
.unwrap();
let expected_comment_vote_views = [
VoteView {
creator: inserted_timmy.clone(),
score: -1,
},
VoteView {
creator: inserted_sara.clone(),
score: 1,
},
];
let read_comment_vote_views = VoteView::list_for_comment(pool, inserted_comment.id, None, None)
.await
.unwrap();
assert_eq!(read_comment_vote_views, expected_comment_vote_views);
// Cleanup
Instance::delete(pool, inserted_instance.id).await.unwrap();
}
}

@ -38,6 +38,7 @@ strum_macros = { workspace = true }
[dev-dependencies]
serial_test = { workspace = true }
tokio = { workspace = true }
pretty_assertions = { workspace = true }
[package.metadata.cargo-machete]
ignored = ["strum"]

@ -159,6 +159,7 @@ mod tests {
use super::*;
use diesel::NotFound;
use lemmy_db_schema::{
assert_length,
source::{
instance::Instance,
local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm},
@ -167,6 +168,7 @@ mod tests {
traits::Crud,
utils::build_db_pool_for_tests,
};
use pretty_assertions::assert_eq;
use serial_test::serial;
struct Data {
@ -256,7 +258,7 @@ mod tests {
.list(pool)
.await
.unwrap();
assert_eq!(list.len(), 1);
assert_length!(1, list);
assert_eq!(list[0].person.id, data.bob.id);
cleanup(data, pool).await;
@ -281,7 +283,7 @@ mod tests {
.unwrap();
let list = PersonView::banned(pool).await.unwrap();
assert_eq!(list.len(), 1);
assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id);
cleanup(data, pool).await;
@ -306,7 +308,7 @@ mod tests {
.unwrap();
let list = PersonView::admins(pool).await.unwrap();
assert_eq!(list.len(), 1);
assert_length!(1, list);
assert_eq!(list[0].person.id, data.alice.id);
let is_admin = PersonView::read(pool, data.alice.id)

@ -1,5 +1,6 @@
[package]
name = "lemmy_federate"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true

@ -23,6 +23,7 @@ static INSTANCES_RECHECK_DELAY: Duration = Duration::from_secs(5);
#[cfg(not(debug_assertions))]
static INSTANCES_RECHECK_DELAY: Duration = Duration::from_secs(60);
#[derive(Clone)]
pub struct Opts {
/// how many processes you are starting in total
pub process_count: i32,
@ -36,7 +37,7 @@ async fn start_stop_federation_workers(
federation_config: FederationConfig<LemmyContext>,
cancel: CancellationToken,
) -> anyhow::Result<()> {
let mut workers = HashMap::<InstanceId, CancellableTask<_>>::new();
let mut workers = HashMap::<InstanceId, CancellableTask>::new();
let (stats_sender, stats_receiver) = unbounded_channel();
let exit_print = tokio::spawn(receive_print_stats(pool.clone(), stats_receiver));
@ -66,40 +67,30 @@ async fn start_stop_federation_workers(
let should_federate = allowed && !is_dead;
if should_federate {
if workers.contains_key(&instance.id) {
if workers
.get(&instance.id)
.map(util::CancellableTask::has_ended)
.unwrap_or(false)
{
// task must have errored out, remove and recreated it
let worker = workers
.remove(&instance.id)
.expect("just checked contains_key");
tracing::error!(
"worker for {} has stopped, recreating: {:?}",
instance.domain,
worker.cancel().await
);
} else {
continue;
}
// worker already running
continue;
}
// create new worker
let config = federation_config.clone();
let stats_sender = stats_sender.clone();
let context = federation_config.to_request_data();
let pool = pool.clone();
workers.insert(
instance.id,
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, |stop| async move {
InstanceWorker::init_and_loop(
instance,
context,
&mut DbPool::Pool(&pool),
stop,
stats_sender,
)
.await?;
Ok(())
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |stop| {
let instance = instance.clone();
let req_data = config.clone().to_request_data();
let stats_sender = stats_sender.clone();
let pool = pool.clone();
async move {
InstanceWorker::init_and_loop(
instance,
req_data,
&mut DbPool::Pool(&pool),
stop,
stats_sender,
)
.await
}
}),
);
} else if !should_federate {
@ -135,9 +126,12 @@ pub fn start_stop_federation_workers_cancellable(
opts: Opts,
pool: ActualDbPool,
config: FederationConfig<LemmyContext>,
) -> CancellableTask<()> {
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |c| {
start_stop_federation_workers(opts, pool, config, c)
) -> CancellableTask {
CancellableTask::spawn(WORKER_EXIT_TIMEOUT, move |stop| {
let opts = opts.clone();
let pool = pool.clone();
let config = config.clone();
async move { start_stop_federation_workers(opts, pool, config, stop).await }
})
}

@ -20,12 +20,7 @@ use moka::future::Cache;
use once_cell::sync::Lazy;
use reqwest::Url;
use serde_json::Value;
use std::{
future::Future,
pin::Pin,
sync::{Arc, RwLock},
time::Duration,
};
use std::{fmt::Debug, future::Future, pin::Pin, sync::Arc, time::Duration};
use tokio::{task::JoinHandle, time::sleep};
use tokio_util::sync::CancellationToken;
@ -49,41 +44,41 @@ pub(crate) static WORK_FINISHED_RECHECK_DELAY: Lazy<Duration> = Lazy::new(|| {
}
});
pub struct CancellableTask<R: Send + 'static> {
f: Pin<Box<dyn Future<Output = Result<R, anyhow::Error>> + Send + 'static>>,
ended: Arc<RwLock<bool>>,
/// A task that will be run in an infinite loop, unless it is cancelled.
/// If the task exits without being cancelled, an error will be logged and the task will be restarted.
pub struct CancellableTask {
f: Pin<Box<dyn Future<Output = Result<(), anyhow::Error>> + Send + 'static>>,
}
impl<R: Send + 'static> CancellableTask<R> {
impl CancellableTask {
/// spawn a task but with graceful shutdown
pub fn spawn<F>(
pub fn spawn<F, R: Debug>(
timeout: Duration,
task: impl FnOnce(CancellationToken) -> F,
) -> CancellableTask<R>
task: impl Fn(CancellationToken) -> F + Send + 'static,
) -> CancellableTask
where
F: Future<Output = Result<R>> + Send + 'static,
F: Future<Output = R> + Send + 'static,
{
let stop = CancellationToken::new();
let task = task(stop.clone());
let ended = Arc::new(RwLock::new(false));
let ended_write = ended.clone();
let task: JoinHandle<Result<R>> = tokio::spawn(async move {
match task.await {
Ok(o) => Ok(o),
Err(e) => {
*ended_write.write().expect("poisoned") = true;
Err(e)
let stop2 = stop.clone();
let task: JoinHandle<()> = tokio::spawn(async move {
loop {
let res = task(stop2.clone()).await;
if stop2.is_cancelled() {
return;
} else {
tracing::warn!("task exited, restarting: {res:?}");
}
}
});
let abort = task.abort_handle();
CancellableTask {
ended,
f: Box::pin(async move {
stop.cancel();
tokio::select! {
r = task => {
Ok(r.context("could not join")??)
r.context("could not join")?;
Ok(())
},
_ = sleep(timeout) => {
abort.abort();
@ -96,12 +91,9 @@ impl<R: Send + 'static> CancellableTask<R> {
}
/// cancel the cancel signal, wait for timeout for the task to stop gracefully, otherwise abort it
pub async fn cancel(self) -> Result<R, anyhow::Error> {
pub async fn cancel(self) -> Result<(), anyhow::Error> {
self.f.await
}
pub fn has_ended(&self) -> bool {
*self.ended.read().expect("poisoned")
}
}
/// assuming apub priv key and ids are immutable, then we don't need to have TTL

@ -157,7 +157,15 @@ impl InstanceWorker {
self.save_and_send_state(pool).await?;
latest_id
};
if id == latest_id {
if id >= latest_id {
if id > latest_id {
tracing::error!(
"{}: last successful id {} is higher than latest id {} in database (did the db get cleared?)",
self.instance.domain,
id.0,
latest_id.0
);
}
// no more work to be done, wait before rechecking
tokio::select! {
() = sleep(*WORK_FINISHED_RECHECK_DELAY) => {},
@ -211,6 +219,7 @@ impl InstanceWorker {
.await
.context("failed figuring out inbox urls")?;
if inbox_urls.is_empty() {
tracing::debug!("{}: {:?} no inboxes", self.instance.domain, activity.id);
self.state.last_successful_id = Some(activity.id);
self.state.last_successful_published_time = Some(activity.published);
return Ok(());

@ -1,5 +1,6 @@
[package]
name = "lemmy_routes"
publish = false
version.workspace = true
edition.workspace = true
description.workspace = true

@ -1,6 +1,6 @@
use lemmy_api_common::{claims::Claims, context::LemmyContext, utils::check_user_valid};
use lemmy_db_views::structs::LocalUserView;
use lemmy_utils::error::LemmyError;
use lemmy_utils::error::{LemmyError, LemmyErrorExt2, LemmyErrorType};
pub mod feeds;
pub mod images;
@ -12,7 +12,9 @@ async fn local_user_view_from_jwt(
jwt: &str,
context: &LemmyContext,
) -> Result<LocalUserView, LemmyError> {
let local_user_id = Claims::validate(jwt, context).await?;
let local_user_id = Claims::validate(jwt, context)
.await
.with_lemmy_type(LemmyErrorType::NotLoggedIn)?;
let local_user_view = LocalUserView::read(&mut context.pool(), local_user_id).await?;
check_user_valid(&local_user_view.person)?;

@ -52,6 +52,7 @@ enum-map = { workspace = true }
[dev-dependencies]
reqwest = { workspace = true }
pretty_assertions = { workspace = true }
[build-dependencies]
rosetta-build = { version = "0.1.3", default-features = false }

@ -16,7 +16,7 @@ pub struct LemmyError {
}
/// Maximum number of items in an array passed as API parameter. See [[LemmyErrorType::TooManyItems]]
pub const MAX_API_PARAM_ELEMENTS: usize = 1000;
pub const MAX_API_PARAM_ELEMENTS: usize = 10_000;
impl<T> From<T> for LemmyError
where
@ -279,6 +279,7 @@ mod tests {
#![allow(clippy::indexing_slicing)]
use super::*;
use actix_web::{body::MessageBody, ResponseError};
use pretty_assertions::assert_eq;
use std::fs::read_to_string;
use strum::IntoEnumIterator;

@ -310,6 +310,7 @@ mod tests {
#![allow(clippy::indexing_slicing)]
use super::{ActionType, BucketConfig, InstantSecs, RateLimitState, RateLimitedGroup};
use pretty_assertions::assert_eq;
#[test]
fn test_split_ipv6() {

@ -46,6 +46,7 @@ mod tests {
Responder,
};
use http::StatusCode;
use pretty_assertions::assert_eq;
#[actix_web::test]
async fn test_non_error_responses_are_not_modified() {

@ -102,7 +102,7 @@ pub struct DatabaseConfig {
pub(crate) connection: DatabaseConnection,
/// Maximum number of active sql connections
#[default(95)]
#[default(30)]
pub pool_size: usize,
}

@ -36,6 +36,7 @@ mod tests {
#![allow(clippy::indexing_slicing)]
use super::*;
use pretty_assertions::assert_eq;
#[test]
fn test_basic_markdown() {

@ -140,6 +140,7 @@ mod tests {
use crate::utils::markdown::spoiler_rule::add;
use markdown_it::MarkdownIt;
use pretty_assertions::assert_eq;
#[test]
fn test_spoiler_markdown() {

@ -39,6 +39,7 @@ mod test {
#![allow(clippy::indexing_slicing)]
use crate::utils::mention::scrape_text_for_mentions;
use pretty_assertions::assert_eq;
#[test]
fn test_mentions_regex() {

@ -69,6 +69,7 @@ mod test {
#![allow(clippy::indexing_slicing)]
use crate::utils::slurs::{remove_slurs, slur_check, slurs_vec_to_str};
use pretty_assertions::assert_eq;
use regex::RegexBuilder;
#[test]

@ -6,7 +6,7 @@ use url::Url;
// From here: https://github.com/vector-im/element-android/blob/develop/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/MatrixPatterns.kt#L35
static VALID_MATRIX_ID_REGEX: Lazy<Regex> = Lazy::new(|| {
Regex::new(r"^@[A-Za-z0-9\\x21-\\x39\\x3B-\\x7F]+:[A-Za-z0-9.-]+(:[0-9]{2,5})?$")
Regex::new(r"^@[A-Za-z0-9\x21-\x39\x3B-\x7F]+:[A-Za-z0-9.-]+(:[0-9]{2,5})?$")
.expect("compile regex")
});
// taken from https://en.wikipedia.org/wiki/UTM_parameters
@ -310,6 +310,7 @@ mod tests {
SITE_NAME_MAX_LENGTH,
},
};
use pretty_assertions::assert_eq;
use url::Url;
#[test]
@ -386,6 +387,7 @@ mod tests {
#[test]
fn test_valid_matrix_id() {
assert!(is_valid_matrix_id("@dess:matrix.org").is_ok());
assert!(is_valid_matrix_id("@dess_:matrix.org").is_ok());
assert!(is_valid_matrix_id("@dess:matrix.org:443").is_ok());
assert!(is_valid_matrix_id("dess:matrix.org").is_err());
assert!(is_valid_matrix_id(" @dess:matrix.org").is_err());

@ -1 +1 @@
Subproject commit 9474a7c0fbc968b8da29251bf5183b7de383cebf
Subproject commit 06a610696af829248954e411ebe2cf3906092244

@ -1,5 +1,5 @@
# syntax=docker/dockerfile:1.6
ARG RUST_VERSION=1.72.1
ARG RUST_VERSION=1.75
ARG CARGO_BUILD_FEATURES=default
ARG RUST_RELEASE_MODE=debug

@ -25,7 +25,7 @@ services:
lemmy:
# use "image" to pull down an already compiled lemmy. make sure to comment out "build".
# image: dessalines/lemmy:0.19.0
# image: dessalines/lemmy:0.19.1
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
# use "build" to build your local lemmy server image for development. make sure to comment out "image".
# run: docker compose up --build
@ -55,7 +55,7 @@ services:
lemmy-ui:
# use "image" to pull down an already compiled lemmy-ui. make sure to comment out "build".
image: dessalines/lemmy-ui:0.19.0
image: dessalines/lemmy-ui:0.19.1
# platform: linux/x86_64 # no arm64 support. uncomment platform if using m1.
# use "build" to build your local lemmy ui image for development. make sure to comment out "image".
# run: docker compose up --build
@ -77,7 +77,7 @@ services:
init: true
pictrs:
image: asonix/pictrs:0.4.0-beta.19
image: asonix/pictrs:0.5.0-rc.2
# this needs to match the pictrs url in lemmy.hjson
hostname: pictrs
# we can set options to pictrs like this, here we set max. image size and forced format for conversion

@ -2,7 +2,7 @@ version: "3.7"
x-ui-default: &ui-default
init: true
image: dessalines/lemmy-ui:0.19.0
image: dessalines/lemmy-ui:0.19.1
# assuming lemmy-ui is cloned besides lemmy directory
# build:
# context: ../../../lemmy-ui
@ -49,7 +49,7 @@ services:
pictrs:
restart: always
image: asonix/pictrs:0.4.0-beta.19
image: asonix/pictrs:0.5.0-rc.2
user: 991:991
volumes:
- ./volumes/pictrs_alpha:/mnt:Z

@ -0,0 +1,3 @@
ALTER TABLE site
ADD CONSTRAINT site_name_key UNIQUE (name);

@ -0,0 +1,3 @@
ALTER TABLE site
DROP CONSTRAINT site_name_key;

@ -45,9 +45,9 @@
## Sobre El Proyecto
| Escritorio | Móvil |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_img.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
| Escritorio | Móvil |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_screen_2.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
[Lemmy](https://github.com/LemmyNet/lemmy) es similar a sitios como [Menéame](https://www.meneame.net/), [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), [Raddle](https://raddle.me), o [Hacker News](https://news.ycombinator.com/): te subscribes a los foros que te interesan, publicas enlaces y debates, luego votas y comentas en ellos. Entre bastidores, es muy diferente; cualquiera puede gestionar fácilmente un servidor, y todos estos servidores son federados (piensa en el correo electrónico), y conectados al mismo universo, llamado [Fediverso](https://es.wikipedia.org/wiki/Fediverso).

@ -47,9 +47,9 @@
## プロジェクトについて
| デスクトップ | モバイル |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_img.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
| デスクトップ | モバイル |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_screen_2.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
[Lemmy](https://github.com/LemmyNet/lemmy) は、[Reddit](https://reddit.com)、[Lobste.rs](https://lobste.rs)、[Hacker News](https://news.ycombinator.com/) といったサイトに似ています。興味のあるフォーラムを購読してリンクや議論を掲載し、投票したり、コメントしたりしています。誰でも簡単にサーバーを運営することができ、これらのサーバーはすべて連合しており(電子メールを考えてください)、[Fediverse](https://en.wikipedia.org/wiki/Fediverse) と呼ばれる同じ宇宙に接続されています。

@ -45,9 +45,9 @@
## О проекте
| Десктоп | Мобильный |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_img.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
| Десктоп | Мобильный |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_screen_2.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
[Lemmy](https://github.com/LemmyNet/lemmy) это аналог таких сайтов как [Reddit](https://reddit.com), [Lobste.rs](https://lobste.rs), или [Hacker News](https://news.ycombinator.com/): вы подписываетесь на форумы, которые вас интересуют , размещаете ссылки и дискутируете, затем голосуете и комментируете их. Однако за кулисами всё совсем по-другому; любой может легко запустить сервер, и все эти серверы объединены (например электронная почта) и подключены к одной вселенной, именуемой [Федиверс](https://ru.wikipedia.org/wiki/Fediverse).

@ -47,9 +47,9 @@
## 关于项目
| 桌面应用 | 移动应用 |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_img.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
| 桌面应用 | 移动应用 |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_screen_2.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
[Lemmy](https://github.com/LemmyNet/lemmy) 与 [Reddit](https://reddit.com)、[Lobste.rs](https://lobste.rs) 或 [Hacker News](https://news.ycombinator.com/) 等网站类似你可以订阅你感兴趣的论坛发布链接和讨论然后进行投票或评论。但在幕后Lemmy 和他们不同——任何人都可以很容易地运行一个服务器,所有服务器都是联邦式的(想想电子邮件),并连接到 [联邦宇宙](https://zh.wikipedia.org/wiki/%E8%81%94%E9%82%A6%E5%AE%87%E5%AE%99)。

@ -48,9 +48,9 @@
## 關於專案
| 桌面設備 | 行動裝置 |
| ---------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_img.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
| 桌面設備 | 行動裝置 |
| --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| ![desktop](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/main_screen_2.webp) | ![mobile](https://raw.githubusercontent.com/LemmyNet/joinlemmy-site/main/src/assets/images/mobile_pic.webp) |
[Lemmy](https://github.com/LemmyNet/lemmy) 與 [Reddit](https://reddit.com)、[Lobste.rs](https://lobste.rs) 或 [Hacker News](https://news.ycombinator.com/) 等網站類似你可以訂閱你感興趣的論壇釋出連結和討論然後進行投票或評論。但在幕後Lemmy 和他們不同——任何人都可以很容易地架設一個伺服器,所有伺服器都是聯邦式的(想想電子郵件),並與 [聯邦宇宙](https://zh.wikipedia.org/wiki/%E8%81%94%E9%82%A6%E5%AE%87%E5%AE%99) 互聯。

@ -1,6 +1,11 @@
use actix_web::{guard, web};
use lemmy_api::{
comment::{distinguish::distinguish_comment, like::like_comment, save::save_comment},
comment::{
distinguish::distinguish_comment,
like::like_comment,
list_comment_likes::list_comment_likes,
save::save_comment,
},
comment_report::{
create::create_comment_report,
list::list_comment_reports,
@ -45,6 +50,7 @@ use lemmy_api::{
feature::feature_post,
get_link_metadata::get_link_metadata,
like::like_post,
list_post_likes::list_post_likes,
lock::lock_post,
mark_read::mark_post_as_read,
save::save_post,
@ -202,6 +208,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.route("/feature", web::post().to(feature_post))
.route("/list", web::get().to(list_posts))
.route("/like", web::post().to(like_post))
.route("/like/list", web::get().to(list_post_likes))
.route("/save", web::put().to(save_post))
.route("/report", web::post().to(create_post_report))
.route("/report/resolve", web::put().to(resolve_post_report))
@ -226,6 +233,7 @@ pub fn config(cfg: &mut web::ServiceConfig, rate_limit: &RateLimitCell) {
.route("/mark_as_read", web::post().to(mark_reply_as_read))
.route("/distinguish", web::post().to(distinguish_comment))
.route("/like", web::post().to(like_comment))
.route("/like/list", web::get().to(list_comment_likes))
.route("/save", web::put().to(save_comment))
.route("/list", web::get().to(list_comments))
.route("/report", web::post().to(create_comment_report))

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save