mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-06-02 07:51:10 +00:00
Merge branch 'main' into arm-docker-image
This commit is contained in:
commit
26296a28f0
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -20,6 +20,8 @@ query_testing/**/reports/*.json
|
|||
api_tests/node_modules
|
||||
api_tests/.yalc
|
||||
api_tests/yalc.lock
|
||||
api_tests/test.png
|
||||
api_tests/pict-rs
|
||||
|
||||
# pictrs data
|
||||
pictrs/
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
# See https://github.com/woodpecker-ci/woodpecker/issues/1677
|
||||
|
||||
variables:
|
||||
- &rust_image "rust:1.72.1"
|
||||
- &rust_image "rust:1.74.0"
|
||||
- &slow_check_paths
|
||||
- path:
|
||||
# rust source code
|
||||
|
@ -163,21 +163,7 @@ steps:
|
|||
commands:
|
||||
# when adding new clippy lints, make sure to also add them in scripts/lint.sh
|
||||
- rustup component add clippy
|
||||
- cargo clippy --workspace --tests --all-targets --features console --
|
||||
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
|
||||
-D clippy::style -D clippy::correctness -D clippy::suspicious
|
||||
-D clippy::dbg_macro -D clippy::inefficient_to_string
|
||||
-D clippy::items-after-statements -D clippy::implicit_clone
|
||||
-D clippy::cast_lossless -D clippy::manual_string_new
|
||||
-D clippy::redundant_closure_for_method_calls
|
||||
-D clippy::unused_self
|
||||
-A clippy::uninlined_format_args
|
||||
-D clippy::get_first
|
||||
-D clippy::explicit_into_iter_loop
|
||||
-D clippy::explicit_iter_loop
|
||||
-D clippy::needless_collect
|
||||
-D clippy::unwrap_used
|
||||
-D clippy::indexing_slicing
|
||||
- cargo clippy --workspace --tests --all-targets --features console -- -D warnings
|
||||
when: *slow_check_paths
|
||||
|
||||
cargo_build:
|
||||
|
|
47
Cargo.lock
generated
47
Cargo.lock
generated
|
@ -10,13 +10,12 @@ checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3"
|
|||
|
||||
[[package]]
|
||||
name = "activitypub_federation"
|
||||
version = "0.5.0-beta.4"
|
||||
version = "0.5.0-beta.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a122cf2c2adf45b164134946bc069659cd93083fab294839a3f1d794b707c17"
|
||||
checksum = "eb3d9484e58e121ce93b84407e077f5888526a8edccb8a8447d14e3ff611aaa3"
|
||||
dependencies = [
|
||||
"activitystreams-kinds",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
"base64 0.21.5",
|
||||
"bytes",
|
||||
|
@ -1997,9 +1996,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.10"
|
||||
version = "0.2.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427"
|
||||
checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
|
@ -2531,7 +2530,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_api"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
|
@ -2559,11 +2558,10 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_api_common"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"encoding",
|
||||
"enum-map",
|
||||
|
@ -2594,7 +2592,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_api_crud"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
|
@ -2612,7 +2610,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_apub"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
|
@ -2651,7 +2649,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_db_schema"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"async-trait",
|
||||
|
@ -2687,7 +2685,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_db_views"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"diesel",
|
||||
|
@ -2705,7 +2703,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_db_views_actor"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"chrono",
|
||||
"diesel",
|
||||
|
@ -2722,7 +2720,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_db_views_moderator"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"diesel",
|
||||
"diesel-async",
|
||||
|
@ -2734,7 +2732,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_federate"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"anyhow",
|
||||
|
@ -2746,7 +2744,6 @@ dependencies = [
|
|||
"lemmy_apub",
|
||||
"lemmy_db_schema",
|
||||
"lemmy_db_views_actor",
|
||||
"lemmy_utils",
|
||||
"moka",
|
||||
"once_cell",
|
||||
"reqwest",
|
||||
|
@ -2758,7 +2755,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_routes"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-web",
|
||||
|
@ -2782,7 +2779,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_server"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"activitypub_federation",
|
||||
"actix-cors",
|
||||
|
@ -2823,7 +2820,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lemmy_utils"
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
dependencies = [
|
||||
"actix-web",
|
||||
"anyhow",
|
||||
|
@ -3307,9 +3304,9 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
|
|||
|
||||
[[package]]
|
||||
name = "openssl"
|
||||
version = "0.10.57"
|
||||
version = "0.10.59"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
|
||||
checksum = "7a257ad03cd8fb16ad4172fedf8094451e1af1c4b70097636ef2eac9a5f0cc33"
|
||||
dependencies = [
|
||||
"bitflags 2.4.1",
|
||||
"cfg-if",
|
||||
|
@ -3339,9 +3336,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
|||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.93"
|
||||
version = "0.9.95"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db4d56a4c0478783083cfafcc42493dd4a981d41669da64b4572a2a089b51b1d"
|
||||
checksum = "40a4130519a360279579c2053038317e40eff64d13fd3f004f9e1b72b8a6aaf9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
|
@ -5357,9 +5354,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-util"
|
||||
version = "0.7.9"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d68074620f57a0b21594d9735eb2e98ab38b17f80d3fcb189fca266771ca60d"
|
||||
checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"futures-core",
|
||||
|
|
51
Cargo.toml
51
Cargo.toml
|
@ -1,5 +1,5 @@
|
|||
[workspace.package]
|
||||
version = "0.19.0-rc.4"
|
||||
version = "0.19.0-rc.5"
|
||||
edition = "2021"
|
||||
description = "A link aggregator for the fediverse"
|
||||
license = "AGPL-3.0"
|
||||
|
@ -20,6 +20,9 @@ repository.workspace = true
|
|||
[lib]
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[profile.release]
|
||||
debug = 0
|
||||
lto = "thin"
|
||||
|
@ -58,18 +61,40 @@ members = [
|
|||
"crates/federate",
|
||||
]
|
||||
|
||||
[workspace.lints.clippy]
|
||||
cast_lossless = "deny"
|
||||
complexity = "deny"
|
||||
correctness = "deny"
|
||||
dbg_macro = "deny"
|
||||
explicit_into_iter_loop = "deny"
|
||||
explicit_iter_loop = "deny"
|
||||
get_first = "deny"
|
||||
implicit_clone = "deny"
|
||||
indexing_slicing = "deny"
|
||||
inefficient_to_string = "deny"
|
||||
items-after-statements = "deny"
|
||||
manual_string_new = "deny"
|
||||
needless_collect = "deny"
|
||||
perf = "deny"
|
||||
redundant_closure_for_method_calls = "deny"
|
||||
style = "deny"
|
||||
suspicious = "deny"
|
||||
uninlined_format_args = "allow"
|
||||
unused_self = "deny"
|
||||
unwrap_used = "deny"
|
||||
|
||||
[workspace.dependencies]
|
||||
lemmy_api = { version = "=0.19.0-rc.4", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.19.0-rc.4", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.19.0-rc.4", path = "./crates/apub" }
|
||||
lemmy_utils = { version = "=0.19.0-rc.4", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.19.0-rc.4", path = "./crates/db_schema" }
|
||||
lemmy_api_common = { version = "=0.19.0-rc.4", path = "./crates/api_common" }
|
||||
lemmy_routes = { version = "=0.19.0-rc.4", path = "./crates/routes" }
|
||||
lemmy_db_views = { version = "=0.19.0-rc.4", path = "./crates/db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.19.0-rc.4", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.19.0-rc.4", path = "./crates/db_views_moderator" }
|
||||
activitypub_federation = { version = "0.5.0-beta.4", default-features = false, features = [
|
||||
lemmy_api = { version = "=0.19.0-rc.5", path = "./crates/api" }
|
||||
lemmy_api_crud = { version = "=0.19.0-rc.5", path = "./crates/api_crud" }
|
||||
lemmy_apub = { version = "=0.19.0-rc.5", path = "./crates/apub" }
|
||||
lemmy_utils = { version = "=0.19.0-rc.5", path = "./crates/utils" }
|
||||
lemmy_db_schema = { version = "=0.19.0-rc.5", path = "./crates/db_schema" }
|
||||
lemmy_api_common = { version = "=0.19.0-rc.5", path = "./crates/api_common" }
|
||||
lemmy_routes = { version = "=0.19.0-rc.5", path = "./crates/routes" }
|
||||
lemmy_db_views = { version = "=0.19.0-rc.5", path = "./crates/db_views" }
|
||||
lemmy_db_views_actor = { version = "=0.19.0-rc.5", path = "./crates/db_views_actor" }
|
||||
lemmy_db_views_moderator = { version = "=0.19.0-rc.5", path = "./crates/db_views_moderator" }
|
||||
activitypub_federation = { version = "0.5.0-beta.5", default-features = false, features = [
|
||||
"actix-web",
|
||||
] }
|
||||
diesel = "2.1.3"
|
||||
|
@ -138,7 +163,7 @@ lemmy_utils = { workspace = true }
|
|||
lemmy_db_schema = { workspace = true }
|
||||
lemmy_api_common = { workspace = true }
|
||||
lemmy_routes = { workspace = true }
|
||||
lemmy_federate = { version = "0.19.0-rc.4", path = "crates/federate" }
|
||||
lemmy_federate = { version = "0.19.0-rc.5", path = "crates/federate" }
|
||||
activitypub_federation = { workspace = true }
|
||||
diesel = { workspace = true }
|
||||
diesel-async = { workspace = true }
|
||||
|
|
|
@ -9,22 +9,25 @@
|
|||
"scripts": {
|
||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
|
||||
"fix": "prettier --write src && eslint --fix src",
|
||||
"api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts",
|
||||
"api-test": "jest -i follow.spec.ts && jest -i post.spec.ts && jest -i comment.spec.ts && jest -i private_message.spec.ts && jest -i user.spec.ts && jest -i community.spec.ts && jest -i image.spec.ts",
|
||||
"api-test-follow": "jest -i follow.spec.ts",
|
||||
"api-test-comment": "jest -i comment.spec.ts",
|
||||
"api-test-post": "jest -i post.spec.ts",
|
||||
"api-test-user": "jest -i user.spec.ts",
|
||||
"api-test-community": "jest -i community.spec.ts",
|
||||
"api-test-private-message": "jest -i private_message.spec.ts"
|
||||
"api-test-private-message": "jest -i private_message.spec.ts",
|
||||
"api-test-image": "jest -i image.spec.ts"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.8",
|
||||
"@types/node": "^20.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"download-file-sync": "^1.0.4",
|
||||
"eslint": "^8.53.0",
|
||||
"eslint-plugin-prettier": "^5.0.1",
|
||||
"jest": "^29.5.0",
|
||||
"lemmy-js-client": "0.19.0-rc.12",
|
||||
"lemmy-js-client": "0.19.0-alpha.18",
|
||||
"prettier": "^3.0.0",
|
||||
"ts-jest": "^29.1.0",
|
||||
"typescript": "^5.0.4"
|
||||
|
|
BIN
api_tests/pict-rs
Executable file
BIN
api_tests/pict-rs
Executable file
Binary file not shown.
|
@ -8,6 +8,17 @@ export RUST_LOG="warn,lemmy_server=debug,lemmy_federate=debug,lemmy_api=debug,le
|
|||
|
||||
export LEMMY_TEST_FAST_FEDERATION=1 # by default, the persistent federation queue has delays in the scale of 30s-5min
|
||||
|
||||
# pictrs setup
|
||||
if ! [ -f "pict-rs" ]; then
|
||||
curl "https://git.asonix.dog/asonix/pict-rs/releases/download/v0.5.0-beta.2/pict-rs-linux-amd64" -o api_tests/pict-rs
|
||||
chmod +x api_tests/pict-rs
|
||||
fi
|
||||
./api_tests/pict-rs \
|
||||
run -a 0.0.0.0:8080 \
|
||||
--danger-dummy-mode \
|
||||
filesystem -p /tmp/pictrs/files \
|
||||
sled -p /tmp/pictrs/sled-repo 2>&1 &
|
||||
|
||||
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
||||
echo "DB URL: ${LEMMY_DATABASE_URL} INSTANCE: $INSTANCE"
|
||||
psql "${LEMMY_DATABASE_URL}/lemmy" -c "DROP DATABASE IF EXISTS $INSTANCE"
|
||||
|
|
|
@ -14,6 +14,8 @@ yarn
|
|||
yarn api-test || true
|
||||
|
||||
killall -s1 lemmy_server || true
|
||||
killall -s1 pict-rs || true
|
||||
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
||||
psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE"
|
||||
done
|
||||
rm -r /tmp/pictrs
|
||||
|
|
|
@ -54,8 +54,8 @@ beforeAll(async () => {
|
|||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await unfollows();
|
||||
afterAll(() => {
|
||||
unfollows();
|
||||
});
|
||||
|
||||
function assertCommentFederation(
|
||||
|
@ -94,7 +94,9 @@ test("Create a comment", async () => {
|
|||
});
|
||||
|
||||
test("Create a comment in a non-existent post", async () => {
|
||||
await expect(createComment(alpha, -1)).rejects.toBe("couldnt_find_post");
|
||||
await expect(createComment(alpha, -1)).rejects.toStrictEqual(
|
||||
Error("couldnt_find_post"),
|
||||
);
|
||||
});
|
||||
|
||||
test("Update a comment", async () => {
|
||||
|
@ -143,7 +145,7 @@ test("Delete a comment", async () => {
|
|||
await waitUntil(
|
||||
() =>
|
||||
resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
|
||||
r => r !== "couldnt_find_object",
|
||||
r => r.message !== "couldnt_find_object",
|
||||
)
|
||||
).comment;
|
||||
if (!gammaComment) {
|
||||
|
@ -160,13 +162,13 @@ test("Delete a comment", async () => {
|
|||
// Make sure that comment is undefined on beta
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
|
||||
e => e === "couldnt_find_object",
|
||||
e => e.message == "couldnt_find_object",
|
||||
);
|
||||
|
||||
// Make sure that comment is undefined on gamma after delete
|
||||
await waitUntil(
|
||||
() => resolveComment(gamma, commentRes.comment_view.comment).catch(e => e),
|
||||
e => e === "couldnt_find_object",
|
||||
e => e.message === "couldnt_find_object",
|
||||
);
|
||||
|
||||
// Test undeleting the comment
|
||||
|
@ -181,7 +183,7 @@ test("Delete a comment", async () => {
|
|||
let betaComment2 = (
|
||||
await waitUntil(
|
||||
() => resolveComment(beta, commentRes.comment_view.comment).catch(e => e),
|
||||
e => e !== "couldnt_find_object",
|
||||
e => e.message !== "couldnt_find_object",
|
||||
)
|
||||
).comment;
|
||||
expect(betaComment2?.comment.deleted).toBe(false);
|
||||
|
|
|
@ -34,9 +34,7 @@ import {
|
|||
} from "./shared";
|
||||
import { EditSite, LemmyHttp } from "lemmy-js-client";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
});
|
||||
beforeAll(setupLogins);
|
||||
|
||||
function assertCommunityFederation(
|
||||
communityOne?: CommunityView,
|
||||
|
@ -66,8 +64,8 @@ test("Create community", async () => {
|
|||
|
||||
// A dupe check
|
||||
let prevName = communityRes.community_view.community.name;
|
||||
await expect(createCommunity(alpha, prevName)).rejects.toBe(
|
||||
"community_already_exists",
|
||||
await expect(createCommunity(alpha, prevName)).rejects.toStrictEqual(
|
||||
Error("community_already_exists"),
|
||||
);
|
||||
|
||||
// Cache the community on beta, make sure it has the other fields
|
||||
|
@ -333,8 +331,8 @@ test("Get community for different casing on domain", async () => {
|
|||
|
||||
// A dupe check
|
||||
let prevName = communityRes.community_view.community.name;
|
||||
await expect(createCommunity(alpha, prevName)).rejects.toBe(
|
||||
"community_already_exists",
|
||||
await expect(createCommunity(alpha, prevName)).rejects.toStrictEqual(
|
||||
Error("community_already_exists"),
|
||||
);
|
||||
|
||||
// Cache the community on beta, make sure it has the other fields
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
jest.setTimeout(120000);
|
||||
|
||||
import { LemmyHttp } from "lemmy-js-client";
|
||||
import {
|
||||
alpha,
|
||||
setupLogins,
|
||||
|
@ -8,14 +9,37 @@ import {
|
|||
unfollowRemotes,
|
||||
getSite,
|
||||
waitUntil,
|
||||
beta,
|
||||
registerUser,
|
||||
betaUrl,
|
||||
} from "./shared";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
beforeAll(setupLogins);
|
||||
|
||||
afterAll(() => {
|
||||
unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await unfollowRemotes(alpha);
|
||||
test("Follow local community", async () => {
|
||||
let userRes = await registerUser(beta);
|
||||
expect(userRes.jwt).toBeDefined();
|
||||
let user = new LemmyHttp(betaUrl, {
|
||||
headers: { Authorization: `Bearer ${userRes.jwt ?? ""}` },
|
||||
});
|
||||
|
||||
let community = (await resolveBetaCommunity(user)).community!;
|
||||
expect(community.counts.subscribers).toBe(1);
|
||||
let follow = await followCommunity(user, true, community.community.id);
|
||||
|
||||
// Make sure the follow response went through
|
||||
expect(follow.community_view.community.local).toBe(true);
|
||||
expect(follow.community_view.subscribed).toBe("Subscribed");
|
||||
expect(follow.community_view.counts.subscribers).toBe(2);
|
||||
|
||||
// Test an unfollow
|
||||
let unfollow = await followCommunity(user, false, community.community.id);
|
||||
expect(unfollow.community_view.subscribed).toBe("NotSubscribed");
|
||||
expect(unfollow.community_view.counts.subscribers).toBe(1);
|
||||
});
|
||||
|
||||
test("Follow federated community", async () => {
|
||||
|
@ -23,7 +47,8 @@ test("Follow federated community", async () => {
|
|||
if (!betaCommunity) {
|
||||
throw "Missing beta community";
|
||||
}
|
||||
await followCommunity(alpha, true, betaCommunity.community.id);
|
||||
let follow = await followCommunity(alpha, true, betaCommunity.community.id);
|
||||
expect(follow.community_view.subscribed).toBe("Pending");
|
||||
betaCommunity = (
|
||||
await waitUntil(
|
||||
() => resolveBetaCommunity(alpha),
|
||||
|
@ -36,6 +61,10 @@ test("Follow federated community", async () => {
|
|||
expect(betaCommunity?.community.name).toBe("main");
|
||||
expect(betaCommunity?.subscribed).toBe("Subscribed");
|
||||
|
||||
// check that unfollow was federated
|
||||
let communityOnBeta1 = await resolveBetaCommunity(beta);
|
||||
expect(communityOnBeta1.community?.counts.subscribers).toBe(2);
|
||||
|
||||
// Check it from local
|
||||
let site = await getSite(alpha);
|
||||
let remoteCommunityId = site.my_user?.follows.find(
|
||||
|
@ -55,4 +84,8 @@ test("Follow federated community", async () => {
|
|||
// Make sure you are unsubbed locally
|
||||
let siteUnfollowCheck = await getSite(alpha);
|
||||
expect(siteUnfollowCheck.my_user?.follows.length).toBe(1);
|
||||
|
||||
// check that unfollow was federated
|
||||
let communityOnBeta2 = await resolveBetaCommunity(beta);
|
||||
expect(communityOnBeta2.community?.counts.subscribers).toBe(1);
|
||||
});
|
||||
|
|
44
api_tests/src/image.spec.ts
Normal file
44
api_tests/src/image.spec.ts
Normal file
|
@ -0,0 +1,44 @@
|
|||
jest.setTimeout(120000);
|
||||
|
||||
import { UploadImage, DeleteImage } from "lemmy-js-client";
|
||||
import { alpha, setupLogins, unfollowRemotes } from "./shared";
|
||||
import fs = require("fs");
|
||||
const downloadFileSync = require("download-file-sync");
|
||||
|
||||
beforeAll(setupLogins);
|
||||
|
||||
afterAll(() => {
|
||||
unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
test("Upload image and delete it", async () => {
|
||||
// upload test image
|
||||
const upload_image = fs.readFileSync("test.png");
|
||||
const upload_form: UploadImage = {
|
||||
image: upload_image,
|
||||
};
|
||||
const upload = await alpha.uploadImage(upload_form);
|
||||
console.log(upload);
|
||||
expect(upload.files![0].file).toBeDefined();
|
||||
expect(upload.files![0].delete_token).toBeDefined();
|
||||
expect(upload.url).toBeDefined();
|
||||
expect(upload.delete_url).toBeDefined();
|
||||
|
||||
// ensure that image download is working. theres probably a better way to do this
|
||||
const content = downloadFileSync(upload.url);
|
||||
expect(content.length).toBeGreaterThan(0);
|
||||
|
||||
// delete image
|
||||
const delete_form: DeleteImage = {
|
||||
token: upload.files![0].delete_token,
|
||||
filename: upload.files![0].file,
|
||||
};
|
||||
const delete_ = await alpha.deleteImage(delete_form);
|
||||
expect(delete_).toBe(true);
|
||||
|
||||
// ensure that image is deleted
|
||||
const content2 = downloadFileSync(upload.url);
|
||||
expect(content2).toBe("");
|
||||
});
|
||||
|
||||
// TODO: add tests for image purging
|
|
@ -50,8 +50,8 @@ beforeAll(async () => {
|
|||
await unfollows();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await unfollows();
|
||||
afterAll(() => {
|
||||
unfollows();
|
||||
});
|
||||
|
||||
function assertPostFederation(postOne?: PostView, postTwo?: PostView) {
|
||||
|
@ -96,18 +96,20 @@ test("Create a post", async () => {
|
|||
assertPostFederation(betaPost, postRes.post_view);
|
||||
|
||||
// Delta only follows beta, so it should not see an alpha ap_id
|
||||
await expect(resolvePost(delta, postRes.post_view.post)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
);
|
||||
await expect(
|
||||
resolvePost(delta, postRes.post_view.post),
|
||||
).rejects.toStrictEqual(Error("couldnt_find_object"));
|
||||
|
||||
// Epsilon has alpha blocked, it should not see the alpha post
|
||||
await expect(resolvePost(epsilon, postRes.post_view.post)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
);
|
||||
await expect(
|
||||
resolvePost(epsilon, postRes.post_view.post),
|
||||
).rejects.toStrictEqual(Error("couldnt_find_object"));
|
||||
});
|
||||
|
||||
test("Create a post in a non-existent community", async () => {
|
||||
await expect(createPost(alpha, -2)).rejects.toBe("couldnt_find_community");
|
||||
await expect(createPost(alpha, -2)).rejects.toStrictEqual(
|
||||
Error("couldnt_find_community"),
|
||||
);
|
||||
});
|
||||
|
||||
test("Unlike a post", async () => {
|
||||
|
@ -157,8 +159,8 @@ test("Update a post", async () => {
|
|||
assertPostFederation(betaPost, updatedPost.post_view);
|
||||
|
||||
// Make sure lemmy beta cannot update the post
|
||||
await expect(editPost(beta, betaPost.post)).rejects.toBe(
|
||||
"no_post_edit_allowed",
|
||||
await expect(editPost(beta, betaPost.post)).rejects.toStrictEqual(
|
||||
Error("no_post_edit_allowed"),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -226,7 +228,9 @@ test("Lock a post", async () => {
|
|||
);
|
||||
|
||||
// Try to make a new comment there, on alpha
|
||||
await expect(createComment(alpha, alphaPost1.post.id)).rejects.toBe("locked");
|
||||
await expect(createComment(alpha, alphaPost1.post.id)).rejects.toStrictEqual(
|
||||
Error("locked"),
|
||||
);
|
||||
|
||||
// Unlock a post
|
||||
let unlockedPost = await lockPost(beta, false, betaPost1.post);
|
||||
|
@ -281,8 +285,8 @@ test("Delete a post", async () => {
|
|||
assertPostFederation(betaPost2, undeletedPost.post_view);
|
||||
|
||||
// Make sure lemmy beta cannot delete the post
|
||||
await expect(deletePost(beta, true, betaPost2.post)).rejects.toBe(
|
||||
"no_post_edit_allowed",
|
||||
await expect(deletePost(beta, true, betaPost2.post)).rejects.toStrictEqual(
|
||||
Error("no_post_edit_allowed"),
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -483,12 +487,12 @@ test.skip("Enforce community ban for federated user", async () => {
|
|||
|
||||
// ensure that the post by alpha got removed
|
||||
await expect(getPost(alpha, searchBeta1.posts[0].post.id)).rejects.toBe(
|
||||
"unknown",
|
||||
Error("unknown"),
|
||||
);
|
||||
|
||||
// Alpha tries to make post on beta, but it fails because of ban
|
||||
await expect(createPost(alpha, betaCommunity.community.id)).rejects.toBe(
|
||||
"banned_from_community",
|
||||
Error("banned_from_community"),
|
||||
);
|
||||
|
||||
// Unban alpha
|
||||
|
|
|
@ -20,8 +20,8 @@ beforeAll(async () => {
|
|||
recipient_id = 3;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await unfollowRemotes(alpha);
|
||||
afterAll(() => {
|
||||
unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
test("Create a private message", async () => {
|
||||
|
|
|
@ -10,6 +10,7 @@ import {
|
|||
InstanceId,
|
||||
LemmyHttp,
|
||||
PostView,
|
||||
SuccessResponse,
|
||||
} from "lemmy-js-client";
|
||||
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
||||
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
|
||||
|
@ -58,7 +59,6 @@ import { Register } from "lemmy-js-client/dist/types/Register";
|
|||
import { SaveUserSettings } from "lemmy-js-client/dist/types/SaveUserSettings";
|
||||
import { DeleteAccount } from "lemmy-js-client/dist/types/DeleteAccount";
|
||||
import { GetSiteResponse } from "lemmy-js-client/dist/types/GetSiteResponse";
|
||||
import { DeleteAccountResponse } from "lemmy-js-client/dist/types/DeleteAccountResponse";
|
||||
import { PrivateMessagesResponse } from "lemmy-js-client/dist/types/PrivateMessagesResponse";
|
||||
import { GetPrivateMessages } from "lemmy-js-client/dist/types/GetPrivateMessages";
|
||||
import { PostReportResponse } from "lemmy-js-client/dist/types/PostReportResponse";
|
||||
|
@ -425,8 +425,9 @@ export async function followCommunity(
|
|||
};
|
||||
const res = await api.followCommunity(form);
|
||||
await waitUntil(
|
||||
() => resolveCommunity(api, res.community_view.community.actor_id),
|
||||
g => g.community?.subscribed === (follow ? "Subscribed" : "NotSubscribed"),
|
||||
() => getCommunity(api, res.community_view.community.id),
|
||||
g =>
|
||||
g.community_view.subscribed === (follow ? "Subscribed" : "NotSubscribed"),
|
||||
);
|
||||
// wait FOLLOW_ADDITIONS_RECHECK_DELAY (there's no API to wait for this currently)
|
||||
await delay(2000);
|
||||
|
@ -637,7 +638,7 @@ export async function loginUser(
|
|||
|
||||
export async function saveUserSettingsBio(
|
||||
api: LemmyHttp,
|
||||
): Promise<LoginResponse> {
|
||||
): Promise<SuccessResponse> {
|
||||
let form: SaveUserSettings = {
|
||||
show_nsfw: true,
|
||||
blur_nsfw: false,
|
||||
|
@ -655,7 +656,7 @@ export async function saveUserSettingsBio(
|
|||
|
||||
export async function saveUserSettingsFederated(
|
||||
api: LemmyHttp,
|
||||
): Promise<LoginResponse> {
|
||||
): Promise<SuccessResponse> {
|
||||
let avatar = "https://image.flaticon.com/icons/png/512/35/35896.png";
|
||||
let banner = "https://image.flaticon.com/icons/png/512/36/35896.png";
|
||||
let bio = "a changed bio";
|
||||
|
@ -679,7 +680,7 @@ export async function saveUserSettingsFederated(
|
|||
export async function saveUserSettings(
|
||||
api: LemmyHttp,
|
||||
form: SaveUserSettings,
|
||||
): Promise<LoginResponse> {
|
||||
): Promise<SuccessResponse> {
|
||||
return api.saveUserSettings(form);
|
||||
}
|
||||
export async function getPersonDetails(
|
||||
|
@ -692,9 +693,7 @@ export async function getPersonDetails(
|
|||
return api.getPersonDetails(form);
|
||||
}
|
||||
|
||||
export async function deleteUser(
|
||||
api: LemmyHttp,
|
||||
): Promise<DeleteAccountResponse> {
|
||||
export async function deleteUser(api: LemmyHttp): Promise<SuccessResponse> {
|
||||
let form: DeleteAccount = {
|
||||
delete_content: true,
|
||||
password,
|
||||
|
|
|
@ -12,19 +12,17 @@ import {
|
|||
createComment,
|
||||
resolveBetaCommunity,
|
||||
deleteUser,
|
||||
resolvePost,
|
||||
resolveComment,
|
||||
saveUserSettingsFederated,
|
||||
setupLogins,
|
||||
alphaUrl,
|
||||
saveUserSettings,
|
||||
getPost,
|
||||
getComments,
|
||||
} from "./shared";
|
||||
import { LemmyHttp, SaveUserSettings } from "lemmy-js-client";
|
||||
import { GetPosts } from "lemmy-js-client/dist/types/GetPosts";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
});
|
||||
beforeAll(setupLogins);
|
||||
|
||||
let apShortname: string;
|
||||
|
||||
|
@ -103,18 +101,22 @@ test("Delete user", async () => {
|
|||
|
||||
await deleteUser(user);
|
||||
|
||||
await expect(resolvePost(alpha, localPost)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
// check that posts and comments are marked as deleted on other instances.
|
||||
// use get methods to avoid refetching from origin instance
|
||||
expect((await getPost(alpha, localPost.id)).post_view.post.deleted).toBe(
|
||||
true,
|
||||
);
|
||||
await expect(resolveComment(alpha, localComment)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
);
|
||||
await expect(resolvePost(alpha, remotePost)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
);
|
||||
await expect(resolveComment(alpha, remoteComment)).rejects.toBe(
|
||||
"couldnt_find_object",
|
||||
expect((await getPost(alpha, remotePost.id)).post_view.post.deleted).toBe(
|
||||
true,
|
||||
);
|
||||
expect(
|
||||
(await getComments(alpha, localComment.post_id)).comments[0].comment
|
||||
.deleted,
|
||||
).toBe(true);
|
||||
expect(
|
||||
(await getComments(alpha, remoteComment.post_id)).comments[0].comment
|
||||
.deleted,
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
test("Requests with invalid auth should be treated as unauthenticated", async () => {
|
||||
|
|
BIN
api_tests/test.png
Normal file
BIN
api_tests/test.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
3147
api_tests/yarn-error.log
Normal file
3147
api_tests/yarn-error.log
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1270,6 +1270,11 @@ doctrine@^3.0.0:
|
|||
dependencies:
|
||||
esutils "^2.0.2"
|
||||
|
||||
download-file-sync@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/download-file-sync/-/download-file-sync-1.0.4.tgz#d3e3c543f836f41039455b9034c72e355b036019"
|
||||
integrity sha512-vH92qNH508jZZA12HQNq/aiMDfagr4JvjFiI17Bi8oYjsxwv5ZVIi7iHkYmUXxOQUr90tcVX+8EPePjAqG1Y0w==
|
||||
|
||||
electron-to-chromium@^1.4.535:
|
||||
version "1.4.537"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.537.tgz#aac4101db53066be1e49baedd000a26bc754adc9"
|
||||
|
@ -2281,10 +2286,10 @@ kleur@^3.0.3:
|
|||
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
|
||||
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
|
||||
|
||||
lemmy-js-client@0.19.0-rc.12:
|
||||
version "0.19.0-rc.12"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-rc.12.tgz#e3bd4e21b1966d583ab790ef70ece8394b012b48"
|
||||
integrity sha512-1iu2fW9vlb3TrI+QR/ODP3+5pWZB0rUqL1wH09IzomDXohCqoQvfmXpwArmgF4Eq8GZgjkcfeMDC2gMrfw/i7Q==
|
||||
lemmy-js-client@0.19.0-alpha.18:
|
||||
version "0.19.0-alpha.18"
|
||||
resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.19.0-alpha.18.tgz#f94841681cabdf9d5c4ce7048eacb57557f68724"
|
||||
integrity sha512-cKJfKKnjK+ijk0Yd6ydtne3Y4FILp2RbQg05pCru9n6PCyPAa85eQL4QxPB1PPed20ckSZRcHLcnr/bYFDgpaw==
|
||||
dependencies:
|
||||
cross-fetch "^3.1.5"
|
||||
form-data "^4.0.0"
|
||||
|
|
|
@ -52,6 +52,8 @@
|
|||
# hotlinking is allowed. If that is the case for your instance, make sure that this setting is
|
||||
# disabled.
|
||||
cache_external_link_previews: true
|
||||
# Timeout for uploading images to pictrs (in seconds)
|
||||
upload_timeout: 30
|
||||
}
|
||||
# Email sending configuration. All options except login/password are mandatory
|
||||
email: {
|
||||
|
|
|
@ -13,6 +13,9 @@ name = "lemmy_api"
|
|||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix_web::web::{Data, Json, Query};
|
|||
use lemmy_api_common::{
|
||||
comment::{ListCommentReports, ListCommentReportsResponse},
|
||||
context::LemmyContext,
|
||||
utils::check_community_mod_action_opt,
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::{comment_report_view::CommentReportQuery, structs::LocalUserView};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -18,7 +18,7 @@ pub async fn list_comment_reports(
|
|||
let community_id = data.community_id;
|
||||
let unresolved_only = data.unresolved_only.unwrap_or_default();
|
||||
|
||||
check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?;
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
|
|
|
@ -45,18 +45,19 @@ pub async fn follow_community(
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
||||
}
|
||||
}
|
||||
if !data.follow {
|
||||
} else {
|
||||
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
||||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
||||
}
|
||||
|
||||
ActivityChannel::submit_activity(
|
||||
SendActivityData::FollowCommunity(community, local_user_view.person.clone(), data.follow),
|
||||
&context,
|
||||
)
|
||||
.await?;
|
||||
if !community.local {
|
||||
ActivityChannel::submit_activity(
|
||||
SendActivityData::FollowCommunity(community, local_user_view.person.clone(), data.follow),
|
||||
&context,
|
||||
)
|
||||
.await?;
|
||||
}
|
||||
|
||||
let community_id = data.community_id;
|
||||
let person_id = local_user_view.person.id;
|
||||
|
|
|
@ -46,11 +46,11 @@ pub async fn ban_from_site(
|
|||
.await
|
||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
|
||||
|
||||
let local_user_id = LocalUserView::read_person(&mut context.pool(), data.person_id)
|
||||
.await?
|
||||
.local_user
|
||||
.id;
|
||||
LoginToken::invalidate_all(&mut context.pool(), local_user_id).await?;
|
||||
// if its a local user, invalidate logins
|
||||
let local_user = LocalUserView::read_person(&mut context.pool(), data.person_id).await;
|
||||
if let Ok(local_user) = local_user {
|
||||
LoginToken::invalidate_all(&mut context.pool(), local_user.local_user.id).await?;
|
||||
}
|
||||
|
||||
// Remove their data if that's desired
|
||||
let remove_data = data.remove_data.unwrap_or(false);
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use actix_web::web::{Data, Json};
|
||||
use actix_web::web::{Data, Json, Query};
|
||||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
person::{GetReportCount, GetReportCountResponse},
|
||||
utils::check_community_mod_action_opt,
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::structs::{
|
||||
CommentReportView,
|
||||
|
@ -14,7 +14,7 @@ use lemmy_utils::error::LemmyError;
|
|||
|
||||
#[tracing::instrument(skip(context))]
|
||||
pub async fn report_count(
|
||||
data: Json<GetReportCount>,
|
||||
data: Query<GetReportCount>,
|
||||
context: Data<LemmyContext>,
|
||||
local_user_view: LocalUserView,
|
||||
) -> Result<Json<GetReportCountResponse>, LemmyError> {
|
||||
|
@ -22,7 +22,7 @@ pub async fn report_count(
|
|||
let admin = local_user_view.local_user.admin;
|
||||
let community_id = data.community_id;
|
||||
|
||||
check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?;
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
|
||||
let comment_reports =
|
||||
CommentReportView::get_report_count(&mut context.pool(), person_id, admin, community_id)
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix_web::web::{Data, Json, Query};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
post::{ListPostReports, ListPostReportsResponse},
|
||||
utils::check_community_mod_action_opt,
|
||||
utils::check_community_mod_of_any_or_admin_action,
|
||||
};
|
||||
use lemmy_db_views::{post_report_view::PostReportQuery, structs::LocalUserView};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -18,7 +18,7 @@ pub async fn list_post_reports(
|
|||
let community_id = data.community_id;
|
||||
let unresolved_only = data.unresolved_only.unwrap_or_default();
|
||||
|
||||
check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool()).await?;
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool()).await?;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
|
|
|
@ -2,7 +2,7 @@ use actix_web::web::{Data, Json, Query};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{GetModlog, GetModlogResponse},
|
||||
utils::{check_community_mod_action_opt, check_private_instance, is_admin},
|
||||
utils::{check_community_mod_of_any_or_admin_action, check_private_instance},
|
||||
};
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, ModlogActionType};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
|
@ -41,11 +41,9 @@ pub async fn get_mod_log(
|
|||
let community_id = data.community_id;
|
||||
|
||||
let is_mod_or_admin = if let Some(local_user_view) = local_user_view {
|
||||
let is_mod = community_id.is_some()
|
||||
&& check_community_mod_action_opt(&local_user_view, community_id, &mut context.pool())
|
||||
.await
|
||||
.is_ok();
|
||||
is_mod || is_admin(&local_user_view).is_ok()
|
||||
check_community_mod_of_any_or_admin_action(&local_user_view, &mut context.pool())
|
||||
.await
|
||||
.is_ok()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
|
|
@ -13,6 +13,9 @@ name = "lemmy_api_common"
|
|||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"tracing",
|
||||
|
@ -56,7 +59,6 @@ webpage = { version = "1.6", default-features = false, features = [
|
|||
"serde",
|
||||
], optional = true }
|
||||
encoding = { version = "0.2.33", optional = true }
|
||||
anyhow = { workspace = true }
|
||||
futures = { workspace = true, optional = true }
|
||||
uuid = { workspace = true, optional = true }
|
||||
tokio = { workspace = true, optional = true }
|
||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
|||
request::purge_image_from_pictrs,
|
||||
site::{FederatedInstances, InstanceWithFederationState},
|
||||
};
|
||||
use anyhow::Context;
|
||||
use chrono::{DateTime, Days, Local, TimeZone, Utc};
|
||||
use enum_map::{enum_map, EnumMap};
|
||||
use lemmy_db_schema::{
|
||||
|
@ -34,7 +33,6 @@ use lemmy_db_views_actor::structs::{
|
|||
use lemmy_utils::{
|
||||
email::{send_email, translations::Lang},
|
||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType, LemmyResult},
|
||||
location_info,
|
||||
rate_limit::{ActionType, BucketConfig},
|
||||
settings::structs::Settings,
|
||||
utils::slurs::build_slur_regex,
|
||||
|
@ -80,6 +78,26 @@ pub async fn is_mod_or_admin_opt(
|
|||
}
|
||||
}
|
||||
|
||||
/// Check that a person is either a mod of any community, or an admin
|
||||
///
|
||||
/// Should only be used for read operations
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_community_mod_of_any_or_admin_action(
|
||||
local_user_view: &LocalUserView,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
let person = &local_user_view.person;
|
||||
|
||||
check_user_valid(person)?;
|
||||
|
||||
let is_mod_of_any_or_admin = CommunityView::is_mod_of_any_or_admin(pool, person.id).await?;
|
||||
if !is_mod_of_any_or_admin {
|
||||
Err(LemmyErrorType::NotAModOrAdmin)?
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
|
||||
check_user_valid(&local_user_view.person)?;
|
||||
if !local_user_view.local_user.admin {
|
||||
|
@ -201,19 +219,6 @@ pub async fn check_community_mod_action(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check_community_mod_action_opt(
|
||||
local_user_view: &LocalUserView,
|
||||
community_id: Option<CommunityId>,
|
||||
pool: &mut DbPool<'_>,
|
||||
) -> LemmyResult<()> {
|
||||
if let Some(community_id) = community_id {
|
||||
check_community_mod_action(&local_user_view.person, community_id, false, pool).await?;
|
||||
} else {
|
||||
is_admin(local_user_view)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn check_post_deleted_or_removed(post: &Post) -> Result<(), LemmyError> {
|
||||
if post.deleted || post.removed {
|
||||
Err(LemmyErrorType::Deleted)?
|
||||
|
@ -786,24 +791,8 @@ pub fn generate_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
|||
Ok(Url::parse(&format!("{actor_id}/inbox"))?.into())
|
||||
}
|
||||
|
||||
pub fn generate_site_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, ParseError> {
|
||||
let mut actor_id: Url = actor_id.clone().into();
|
||||
actor_id.set_path("site_inbox");
|
||||
Ok(actor_id.into())
|
||||
}
|
||||
|
||||
pub fn generate_shared_inbox_url(actor_id: &DbUrl) -> Result<DbUrl, LemmyError> {
|
||||
let actor_id: Url = actor_id.clone().into();
|
||||
let url = format!(
|
||||
"{}://{}{}/inbox",
|
||||
&actor_id.scheme(),
|
||||
&actor_id.host_str().context(location_info!())?,
|
||||
if let Some(port) = actor_id.port() {
|
||||
format!(":{port}")
|
||||
} else {
|
||||
String::new()
|
||||
},
|
||||
);
|
||||
pub fn generate_shared_inbox_url(settings: &Settings) -> Result<DbUrl, LemmyError> {
|
||||
let url = format!("{}/inbox", settings.get_protocol_and_hostname());
|
||||
Ok(Url::parse(&url)?.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ homepage.workspace = true
|
|||
documentation.workspace = true
|
||||
repository.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -90,7 +90,7 @@ pub async fn create_community(
|
|||
.public_key(keypair.public_key)
|
||||
.followers_url(Some(generate_followers_url(&community_actor_id)?))
|
||||
.inbox_url(Some(generate_inbox_url(&community_actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
|
||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||
.instance_id(site_view.site.instance_id)
|
||||
.build();
|
||||
|
|
|
@ -4,7 +4,7 @@ use actix_web::web::{Data, Json};
|
|||
use lemmy_api_common::{
|
||||
context::LemmyContext,
|
||||
site::{CreateSite, SiteResponse},
|
||||
utils::{generate_site_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config},
|
||||
utils::{generate_shared_inbox_url, is_admin, local_site_rate_limit_to_rate_limit_config},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::DbUrl,
|
||||
|
@ -47,7 +47,7 @@ pub async fn create_site(
|
|||
validate_create_payload(&local_site, &data)?;
|
||||
|
||||
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
||||
let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
|
||||
let inbox_url = Some(generate_shared_inbox_url(context.settings())?);
|
||||
let keypair = generate_actor_keypair()?;
|
||||
|
||||
let site_form = SiteUpdateForm {
|
||||
|
|
|
@ -113,7 +113,7 @@ pub async fn register(
|
|||
.private_key(Some(actor_keypair.private_key))
|
||||
.public_key(actor_keypair.public_key)
|
||||
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(context.settings())?))
|
||||
.instance_id(site_view.site.instance_id)
|
||||
.build();
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ name = "lemmy_apub"
|
|||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[dependencies]
|
||||
lemmy_utils = { workspace = true }
|
||||
lemmy_db_schema = { workspace = true, features = ["full"] }
|
||||
|
|
|
@ -66,7 +66,14 @@ impl ActivityHandler for Delete {
|
|||
)
|
||||
.await
|
||||
} else {
|
||||
receive_delete_action(self.object.id(), &self.actor, true, context).await
|
||||
receive_delete_action(
|
||||
self.object.id(),
|
||||
&self.actor,
|
||||
true,
|
||||
self.remove_data,
|
||||
context,
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +101,7 @@ impl Delete {
|
|||
summary,
|
||||
id,
|
||||
audience: community.map(|c| c.actor_id.clone().into()),
|
||||
remove_data: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +172,7 @@ pub(in crate::activities) async fn receive_remove_action(
|
|||
.await?;
|
||||
}
|
||||
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
||||
DeletableObjects::Person { .. } => unimplemented!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
||||
insert_received_activity,
|
||||
objects::person::ApubPerson,
|
||||
protocol::activities::deletion::delete_user::DeleteUser,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
config::Data,
|
||||
kinds::{activity::DeleteType, public},
|
||||
protocol::verification::verify_urls_match,
|
||||
traits::{ActivityHandler, Actor},
|
||||
};
|
||||
use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
|
||||
use lemmy_db_schema::source::{activity::ActivitySendTargets, person::Person};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
pub async fn delete_user(
|
||||
person: Person,
|
||||
delete_content: bool,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let actor: ApubPerson = person.into();
|
||||
|
||||
let id = generate_activity_id(
|
||||
DeleteType::Delete,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?;
|
||||
let delete = DeleteUser {
|
||||
actor: actor.id().into(),
|
||||
to: vec![public()],
|
||||
object: actor.id().into(),
|
||||
kind: DeleteType::Delete,
|
||||
id: id.clone(),
|
||||
cc: vec![],
|
||||
remove_data: Some(delete_content),
|
||||
};
|
||||
|
||||
let inboxes = ActivitySendTargets::to_all_instances();
|
||||
|
||||
send_lemmy_activity(&context, delete, &actor, inboxes, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// This can be separate from Delete activity because it doesn't need to be handled in shared inbox
|
||||
/// (cause instance actor doesn't have shared inbox).
|
||||
#[async_trait::async_trait]
|
||||
impl ActivityHandler for DeleteUser {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn id(&self) -> &Url {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn actor(&self) -> &Url {
|
||||
self.actor.inner()
|
||||
}
|
||||
|
||||
async fn verify(&self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
insert_received_activity(&self.id, context).await?;
|
||||
verify_is_public(&self.to, &[])?;
|
||||
verify_person(&self.actor, context).await?;
|
||||
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn receive(self, context: &Data<Self::DataType>) -> Result<(), LemmyError> {
|
||||
let actor = self.actor.dereference(context).await?;
|
||||
if self.remove_data.unwrap_or(false) {
|
||||
purge_user_account(actor.id, context).await?;
|
||||
} else {
|
||||
Person::delete_account(&mut context.pool(), actor.id).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -24,10 +24,10 @@ use activitypub_federation::{
|
|||
config::Data,
|
||||
fetch::object_id::ObjectId,
|
||||
kinds::public,
|
||||
protocol::verification::verify_domains_match,
|
||||
protocol::verification::{verify_domains_match, verify_urls_match},
|
||||
traits::{Actor, Object},
|
||||
};
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_api_common::{context::LemmyContext, utils::purge_user_account};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{
|
||||
|
@ -45,7 +45,6 @@ use std::ops::Deref;
|
|||
use url::Url;
|
||||
|
||||
pub mod delete;
|
||||
pub mod delete_user;
|
||||
pub mod undo_delete;
|
||||
|
||||
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
||||
|
@ -135,8 +134,26 @@ pub(crate) async fn send_apub_delete_private_message(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn send_apub_delete_user(
|
||||
person: Person,
|
||||
remove_data: bool,
|
||||
context: Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let person: ApubPerson = person.into();
|
||||
|
||||
let deletable = DeletableObjects::Person(person.clone());
|
||||
let mut delete: Delete = Delete::new(&person, deletable, public(), None, None, &context)?;
|
||||
delete.remove_data = Some(remove_data);
|
||||
|
||||
let inboxes = ActivitySendTargets::to_all_instances();
|
||||
|
||||
send_lemmy_activity(&context, delete, &person, inboxes, true).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub enum DeletableObjects {
|
||||
Community(ApubCommunity),
|
||||
Person(ApubPerson),
|
||||
Comment(ApubComment),
|
||||
Post(ApubPost),
|
||||
PrivateMessage(ApubPrivateMessage),
|
||||
|
@ -151,6 +168,9 @@ impl DeletableObjects {
|
|||
if let Some(c) = ApubCommunity::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Community(c));
|
||||
}
|
||||
if let Some(p) = ApubPerson::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Person(p));
|
||||
}
|
||||
if let Some(p) = ApubPost::read_from_id(ap_id.clone(), context).await? {
|
||||
return Ok(DeletableObjects::Post(p));
|
||||
}
|
||||
|
@ -166,6 +186,7 @@ impl DeletableObjects {
|
|||
pub(crate) fn id(&self) -> Url {
|
||||
match self {
|
||||
DeletableObjects::Community(c) => c.id(),
|
||||
DeletableObjects::Person(p) => p.id(),
|
||||
DeletableObjects::Comment(c) => c.ap_id.clone().into(),
|
||||
DeletableObjects::Post(p) => p.ap_id.clone().into(),
|
||||
DeletableObjects::PrivateMessage(p) => p.ap_id.clone().into(),
|
||||
|
@ -191,6 +212,11 @@ pub(in crate::activities) async fn verify_delete_activity(
|
|||
// community deletion is always a mod (or admin) action
|
||||
verify_mod_action(&activity.actor, &community, context).await?;
|
||||
}
|
||||
DeletableObjects::Person(person) => {
|
||||
verify_is_public(&activity.to, &[])?;
|
||||
verify_person(&activity.actor, context).await?;
|
||||
verify_urls_match(person.actor_id.inner(), activity.object.id())?;
|
||||
}
|
||||
DeletableObjects::Post(p) => {
|
||||
verify_is_public(&activity.to, &[])?;
|
||||
verify_delete_post_or_comment(
|
||||
|
@ -245,6 +271,7 @@ async fn receive_delete_action(
|
|||
object: &Url,
|
||||
actor: &ObjectId<ApubPerson>,
|
||||
deleted: bool,
|
||||
do_purge_user_account: Option<bool>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
match DeletableObjects::read_from_db(object, context).await? {
|
||||
|
@ -266,6 +293,13 @@ async fn receive_delete_action(
|
|||
)
|
||||
.await?;
|
||||
}
|
||||
DeletableObjects::Person(person) => {
|
||||
if do_purge_user_account.unwrap_or(false) {
|
||||
purge_user_account(person.id, context).await?;
|
||||
} else {
|
||||
Person::delete_account(&mut context.pool(), person.id).await?;
|
||||
}
|
||||
}
|
||||
DeletableObjects::Post(post) => {
|
||||
if deleted != post.deleted {
|
||||
Post::update(
|
||||
|
|
|
@ -58,7 +58,7 @@ impl ActivityHandler for UndoDelete {
|
|||
)
|
||||
.await
|
||||
} else {
|
||||
receive_delete_action(self.object.object.id(), &self.actor, false, context).await
|
||||
receive_delete_action(self.object.object.id(), &self.actor, false, None, context).await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -156,6 +156,7 @@ impl UndoDelete {
|
|||
.await?;
|
||||
}
|
||||
DeletableObjects::PrivateMessage(_) => unimplemented!(),
|
||||
DeletableObjects::Person { .. } => unimplemented!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -52,15 +52,6 @@ impl Follow {
|
|||
community: &ApubCommunity,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> Result<(), LemmyError> {
|
||||
let community_follower_form = CommunityFollowerForm {
|
||||
community_id: community.id,
|
||||
person_id: actor.id,
|
||||
pending: true,
|
||||
};
|
||||
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let follow = Follow::new(actor, community, context)?;
|
||||
let inbox = if community.local {
|
||||
ActivitySendTargets::empty()
|
||||
|
|
|
@ -9,10 +9,10 @@ use crate::{
|
|||
},
|
||||
create_or_update::private_message::send_create_or_update_pm,
|
||||
deletion::{
|
||||
delete_user::delete_user,
|
||||
send_apub_delete_in_community,
|
||||
send_apub_delete_in_community_new,
|
||||
send_apub_delete_private_message,
|
||||
send_apub_delete_user,
|
||||
DeletableObjects,
|
||||
},
|
||||
voting::send_like_activity,
|
||||
|
@ -330,7 +330,7 @@ pub async fn match_outgoing_activities(
|
|||
DeletePrivateMessage(person, pm, deleted) => {
|
||||
send_apub_delete_private_message(&person.into(), pm, deleted, context).await
|
||||
}
|
||||
DeleteUser(person, delete_content) => delete_user(person, delete_content, context).await,
|
||||
DeleteUser(person, remove_data) => send_apub_delete_user(person, remove_data, context).await,
|
||||
CreateReport(url, actor, community, reason) => {
|
||||
Report::send(ObjectId::from(url), actor, community, reason, context).await
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
note::CreateOrUpdateNote,
|
||||
page::CreateOrUpdatePage,
|
||||
},
|
||||
deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||
deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
||||
voting::{undo_vote::UndoVote, vote::Vote},
|
||||
},
|
||||
|
@ -98,16 +98,6 @@ pub enum AnnouncableActivities {
|
|||
Page(Page),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[enum_delegate::implement(ActivityHandler)]
|
||||
#[allow(clippy::enum_variant_names)]
|
||||
pub enum SiteInboxActivities {
|
||||
BlockUser(BlockUser),
|
||||
UndoBlockUser(UndoBlockUser),
|
||||
DeleteUser(DeleteUser),
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl InCommunity for AnnouncableActivities {
|
||||
#[tracing::instrument(skip(self, context))]
|
||||
|
@ -134,44 +124,43 @@ impl InCommunity for AnnouncableActivities {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::{
|
||||
activity_lists::{GroupInboxActivities, PersonInboxActivities, SiteInboxActivities},
|
||||
activity_lists::{GroupInboxActivities, PersonInboxActivities, SharedInboxActivities},
|
||||
protocol::tests::{test_json, test_parse_lemmy_item},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_group_inbox() {
|
||||
test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json")
|
||||
.unwrap();
|
||||
fn test_group_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<GroupInboxActivities>("assets/lemmy/activities/following/follow.json")?;
|
||||
test_parse_lemmy_item::<GroupInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_person_inbox() {
|
||||
test_parse_lemmy_item::<PersonInboxActivities>("assets/lemmy/activities/following/accept.json")
|
||||
.unwrap();
|
||||
fn test_person_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/following/accept.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
test_parse_lemmy_item::<PersonInboxActivities>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)
|
||||
.unwrap();
|
||||
test_json::<PersonInboxActivities>("assets/mastodon/activities/follow.json").unwrap();
|
||||
)?;
|
||||
test_json::<PersonInboxActivities>("assets/mastodon/activities/follow.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_site_inbox() {
|
||||
test_parse_lemmy_item::<SiteInboxActivities>(
|
||||
fn test_shared_inbox() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<SharedInboxActivities>(
|
||||
"assets/lemmy/activities/deletion/delete_user.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,8 @@ use lemmy_db_schema::{
|
|||
comment::{CommentSaved, CommentSavedForm},
|
||||
community::{CommunityFollower, CommunityFollowerForm},
|
||||
community_block::{CommunityBlock, CommunityBlockForm},
|
||||
instance::Instance,
|
||||
instance_block::{InstanceBlock, InstanceBlockForm},
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
person_block::{PersonBlock, PersonBlockForm},
|
||||
|
@ -58,6 +60,8 @@ pub struct UserSettingsBackup {
|
|||
pub blocked_communities: Vec<ObjectId<ApubCommunity>>,
|
||||
#[serde(default)]
|
||||
pub blocked_users: Vec<ObjectId<ApubPerson>>,
|
||||
#[serde(default)]
|
||||
pub blocked_instances: Vec<String>,
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(context))]
|
||||
|
@ -78,6 +82,7 @@ pub async fn export_settings(
|
|||
settings: Some(local_user_view.local_user),
|
||||
followed_communities: vec_into(lists.followed_communities),
|
||||
blocked_communities: vec_into(lists.blocked_communities),
|
||||
blocked_instances: lists.blocked_instances,
|
||||
blocked_users: lists.blocked_users.into_iter().map(Into::into).collect(),
|
||||
saved_posts: lists.saved_posts.into_iter().map(Into::into).collect(),
|
||||
saved_comments: lists.saved_comments.into_iter().map(Into::into).collect(),
|
||||
|
@ -130,6 +135,7 @@ pub async fn import_settings(
|
|||
let url_count = data.followed_communities.len()
|
||||
+ data.blocked_communities.len()
|
||||
+ data.blocked_users.len()
|
||||
+ data.blocked_instances.len()
|
||||
+ data.saved_posts.len()
|
||||
+ data.saved_comments.len();
|
||||
if url_count > MAX_API_PARAM_ELEMENTS {
|
||||
|
@ -269,6 +275,19 @@ pub async fn import_settings(
|
|||
LemmyResult::Ok(())
|
||||
}))
|
||||
.await?;
|
||||
|
||||
try_join_all(data.blocked_instances.iter().map(|domain| async {
|
||||
// dont fetch unknown blocked objects from home server
|
||||
let instance = Instance::read_or_create(&mut context.pool(), domain.clone()).await?;
|
||||
let form = InstanceBlockForm {
|
||||
person_id,
|
||||
instance_id: instance.id,
|
||||
};
|
||||
InstanceBlock::block(&mut context.pool(), &form).await?;
|
||||
LemmyResult::Ok(())
|
||||
}))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
});
|
||||
|
||||
|
@ -277,7 +296,6 @@ pub async fn import_settings(
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::{
|
||||
|
@ -297,7 +315,7 @@ mod tests {
|
|||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_utils::error::LemmyErrorType;
|
||||
use lemmy_utils::error::{LemmyErrorType, LemmyResult};
|
||||
use serial_test::serial;
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
|
@ -306,10 +324,8 @@ mod tests {
|
|||
name: String,
|
||||
bio: Option<String>,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LocalUserView {
|
||||
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
) -> LemmyResult<LocalUserView> {
|
||||
let instance = Instance::read_or_create(&mut context.pool(), "example.com".to_string()).await?;
|
||||
let person_form = PersonInsertForm::builder()
|
||||
.name(name.clone())
|
||||
.display_name(Some(name.clone()))
|
||||
|
@ -317,63 +333,49 @@ mod tests {
|
|||
.public_key("asd".to_string())
|
||||
.instance_id(instance.id)
|
||||
.build();
|
||||
let person = Person::create(&mut context.pool(), &person_form)
|
||||
.await
|
||||
.unwrap();
|
||||
let person = Person::create(&mut context.pool(), &person_form).await?;
|
||||
|
||||
let user_form = LocalUserInsertForm::builder()
|
||||
.person_id(person.id)
|
||||
.password_encrypted("pass".to_string())
|
||||
.build();
|
||||
let local_user = LocalUser::create(&mut context.pool(), &user_form)
|
||||
.await
|
||||
.unwrap();
|
||||
let local_user = LocalUser::create(&mut context.pool(), &user_form).await?;
|
||||
|
||||
LocalUserView::read(&mut context.pool(), local_user.id)
|
||||
.await
|
||||
.unwrap()
|
||||
Ok(LocalUserView::read(&mut context.pool(), local_user.id).await?)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_settings_export_import() {
|
||||
let context = init_context().await;
|
||||
async fn test_settings_export_import() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
|
||||
let export_user = create_user("hanna".to_string(), Some("my bio".to_string()), &context).await;
|
||||
let export_user =
|
||||
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
|
||||
|
||||
let community_form = CommunityInsertForm::builder()
|
||||
.name("testcom".to_string())
|
||||
.title("testcom".to_string())
|
||||
.instance_id(export_user.person.instance_id)
|
||||
.build();
|
||||
let community = Community::create(&mut context.pool(), &community_form)
|
||||
.await
|
||||
.unwrap();
|
||||
let community = Community::create(&mut context.pool(), &community_form).await?;
|
||||
let follower_form = CommunityFollowerForm {
|
||||
community_id: community.id,
|
||||
person_id: export_user.person.id,
|
||||
pending: false,
|
||||
};
|
||||
CommunityFollower::follow(&mut context.pool(), &follower_form)
|
||||
.await
|
||||
.unwrap();
|
||||
CommunityFollower::follow(&mut context.pool(), &follower_form).await?;
|
||||
|
||||
let backup = export_settings(export_user.clone(), context.reset_request_count())
|
||||
.await
|
||||
.unwrap();
|
||||
let backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
|
||||
|
||||
let import_user = create_user("charles".to_string(), None, &context).await;
|
||||
let import_user = create_user("charles".to_string(), None, &context).await?;
|
||||
|
||||
import_settings(backup, import_user.clone(), context.reset_request_count())
|
||||
.await
|
||||
.unwrap();
|
||||
import_settings(backup, import_user.clone(), context.reset_request_count()).await?;
|
||||
|
||||
// wait for background task to finish
|
||||
sleep(Duration::from_millis(1000)).await;
|
||||
|
||||
let import_user_updated = LocalUserView::read(&mut context.pool(), import_user.local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
let import_user_updated =
|
||||
LocalUserView::read(&mut context.pool(), import_user.local_user.id).await?;
|
||||
|
||||
assert_eq!(
|
||||
export_user.person.display_name,
|
||||
|
@ -381,61 +383,49 @@ mod tests {
|
|||
);
|
||||
assert_eq!(export_user.person.bio, import_user_updated.person.bio);
|
||||
|
||||
let follows = CommunityFollowerView::for_person(&mut context.pool(), import_user.person.id)
|
||||
.await
|
||||
.unwrap();
|
||||
let follows =
|
||||
CommunityFollowerView::for_person(&mut context.pool(), import_user.person.id).await?;
|
||||
assert_eq!(follows.len(), 1);
|
||||
assert_eq!(follows[0].community.actor_id, community.actor_id);
|
||||
|
||||
LocalUser::delete(&mut context.pool(), export_user.local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUser::delete(&mut context.pool(), import_user.local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUser::delete(&mut context.pool(), export_user.local_user.id).await?;
|
||||
LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn disallow_large_backup() {
|
||||
let context = init_context().await;
|
||||
async fn disallow_large_backup() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
|
||||
let export_user = create_user("hanna".to_string(), Some("my bio".to_string()), &context).await;
|
||||
let export_user =
|
||||
create_user("hanna".to_string(), Some("my bio".to_string()), &context).await?;
|
||||
|
||||
let mut backup = export_settings(export_user.clone(), context.reset_request_count())
|
||||
.await
|
||||
.unwrap();
|
||||
let mut backup = export_settings(export_user.clone(), context.reset_request_count()).await?;
|
||||
|
||||
for _ in 0..251 {
|
||||
backup
|
||||
.followed_communities
|
||||
.push("http://example.com".parse().unwrap());
|
||||
.push("http://example.com".parse()?);
|
||||
backup
|
||||
.blocked_communities
|
||||
.push("http://example2.com".parse().unwrap());
|
||||
backup
|
||||
.saved_posts
|
||||
.push("http://example3.com".parse().unwrap());
|
||||
backup
|
||||
.saved_comments
|
||||
.push("http://example4.com".parse().unwrap());
|
||||
.push("http://example2.com".parse()?);
|
||||
backup.saved_posts.push("http://example3.com".parse()?);
|
||||
backup.saved_comments.push("http://example4.com".parse()?);
|
||||
}
|
||||
|
||||
let import_user = create_user("charles".to_string(), None, &context).await;
|
||||
let import_user = create_user("charles".to_string(), None, &context).await?;
|
||||
|
||||
let imported =
|
||||
import_settings(backup, import_user.clone(), context.reset_request_count()).await;
|
||||
|
||||
assert_eq!(
|
||||
imported.err().unwrap().error_type,
|
||||
LemmyErrorType::TooManyItems
|
||||
imported.err().map(|e| e.error_type),
|
||||
Some(LemmyErrorType::TooManyItems)
|
||||
);
|
||||
|
||||
LocalUser::delete(&mut context.pool(), export_user.local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUser::delete(&mut context.pool(), import_user.local_user.id)
|
||||
.await
|
||||
.unwrap();
|
||||
LocalUser::delete(&mut context.pool(), export_user.local_user.id).await?;
|
||||
LocalUser::delete(&mut context.pool(), import_user.local_user.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,6 @@ impl Collection for ApubCommunityModerators {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
|
@ -123,20 +122,19 @@ mod tests {
|
|||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_community_moderators() {
|
||||
let context = init_context().await;
|
||||
let (new_mod, site) = parse_lemmy_person(&context).await;
|
||||
let community = parse_lemmy_community(&context).await;
|
||||
async fn test_parse_lemmy_community_moderators() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let (new_mod, site) = parse_lemmy_person(&context).await?;
|
||||
let community = parse_lemmy_community(&context).await?;
|
||||
let community_id = community.id;
|
||||
|
||||
let inserted_instance =
|
||||
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
Instance::read_or_create(&mut context.pool(), "my_domain.tld".to_string()).await?;
|
||||
|
||||
let old_mod = PersonInsertForm::builder()
|
||||
.name("holly".into())
|
||||
|
@ -144,49 +142,34 @@ mod tests {
|
|||
.instance_id(inserted_instance.id)
|
||||
.build();
|
||||
|
||||
let old_mod = Person::create(&mut context.pool(), &old_mod).await.unwrap();
|
||||
let old_mod = Person::create(&mut context.pool(), &old_mod).await?;
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
person_id: old_mod.id,
|
||||
};
|
||||
|
||||
CommunityModerator::join(&mut context.pool(), &community_moderator_form)
|
||||
.await
|
||||
.unwrap();
|
||||
CommunityModerator::join(&mut context.pool(), &community_moderator_form).await?;
|
||||
|
||||
assert_eq!(site.actor_id.to_string(), "https://enterprise.lemmy.ml/");
|
||||
|
||||
let json: GroupModerators =
|
||||
file_to_json_object("assets/lemmy/collections/group_moderators.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||
ApubCommunityModerators::verify(&json, &url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubCommunityModerators::from_json(json, &community, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
file_to_json_object("assets/lemmy/collections/group_moderators.json")?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward")?;
|
||||
ApubCommunityModerators::verify(&json, &url, &context).await?;
|
||||
ApubCommunityModerators::from_json(json, &community, &context).await?;
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let current_moderators =
|
||||
CommunityModeratorView::for_community(&mut context.pool(), community_id)
|
||||
.await
|
||||
.unwrap();
|
||||
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||
|
||||
assert_eq!(current_moderators.len(), 1);
|
||||
assert_eq!(current_moderators[0].moderator.id, new_mod.id);
|
||||
|
||||
Person::delete(&mut context.pool(), old_mod.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Person::delete(&mut context.pool(), new_mod.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Community::delete(&mut context.pool(), community.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), site.id).await.unwrap();
|
||||
Instance::delete(&mut context.pool(), inserted_instance.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Person::delete(&mut context.pool(), old_mod.id).await?;
|
||||
Person::delete(&mut context.pool(), new_mod.id).await?;
|
||||
Community::delete(&mut context.pool(), community.id).await?;
|
||||
Site::delete(&mut context.pool(), site.id).await?;
|
||||
Instance::delete(&mut context.pool(), inserted_instance.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ use crate::http::{
|
|||
person::{get_apub_person_http, get_apub_person_outbox, person_inbox},
|
||||
post::get_apub_post,
|
||||
shared_inbox,
|
||||
site::{get_apub_site_http, get_apub_site_inbox, get_apub_site_outbox},
|
||||
site::{get_apub_site_http, get_apub_site_outbox},
|
||||
};
|
||||
use actix_web::{
|
||||
guard::{Guard, GuardContext},
|
||||
|
@ -58,8 +58,7 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
|||
.guard(InboxRequestGuard)
|
||||
.route("/c/{community_name}/inbox", web::post().to(community_inbox))
|
||||
.route("/u/{user_name}/inbox", web::post().to(person_inbox))
|
||||
.route("/inbox", web::post().to(shared_inbox))
|
||||
.route("/site_inbox", web::post().to(get_apub_site_inbox)),
|
||||
.route("/inbox", web::post().to(shared_inbox)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
use crate::{
|
||||
activity_lists::SiteInboxActivities,
|
||||
http::create_apub_response,
|
||||
objects::{instance::ApubSite, person::ApubPerson},
|
||||
objects::instance::ApubSite,
|
||||
protocol::collections::empty_outbox::EmptyOutbox,
|
||||
};
|
||||
use activitypub_federation::{
|
||||
actix_web::inbox::receive_activity,
|
||||
config::Data,
|
||||
protocol::context::WithContext,
|
||||
traits::Object,
|
||||
};
|
||||
use actix_web::{web::Bytes, HttpRequest, HttpResponse};
|
||||
use activitypub_federation::{config::Data, traits::Object};
|
||||
use actix_web::HttpResponse;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -36,15 +30,3 @@ pub(crate) async fn get_apub_site_outbox(
|
|||
let outbox = EmptyOutbox::new(Url::parse(&outbox_id)?)?;
|
||||
create_apub_response(&outbox)
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn get_apub_site_inbox(
|
||||
request: HttpRequest,
|
||||
body: Bytes,
|
||||
data: Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_activity::<WithContext<SiteInboxActivities>, ApubPerson, LemmyContext>(
|
||||
request, body, &data,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::fetcher::post_or_comment::PostOrComment;
|
||||
use activitypub_federation::config::{Data, UrlVerifier};
|
||||
use anyhow::anyhow;
|
||||
use activitypub_federation::{
|
||||
config::{Data, UrlVerifier},
|
||||
error::Error as ActivityPubError,
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use lemmy_api_common::context::LemmyContext;
|
||||
use lemmy_db_schema::{
|
||||
|
@ -39,7 +41,7 @@ pub struct VerifyUrlData(pub ActualDbPool);
|
|||
|
||||
#[async_trait]
|
||||
impl UrlVerifier for VerifyUrlData {
|
||||
async fn verify(&self, url: &Url) -> Result<(), anyhow::Error> {
|
||||
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");
|
||||
|
@ -47,16 +49,16 @@ impl UrlVerifier for VerifyUrlData {
|
|||
LemmyError {
|
||||
error_type: LemmyErrorType::FederationDisabled,
|
||||
..
|
||||
} => anyhow!("Federation disabled"),
|
||||
} => ActivityPubError::Other("Federation disabled".into()),
|
||||
LemmyError {
|
||||
error_type: LemmyErrorType::DomainBlocked(domain),
|
||||
..
|
||||
} => anyhow!("Domain {domain:?} is blocked"),
|
||||
} => ActivityPubError::Other(format!("Domain {domain:?} is blocked")),
|
||||
LemmyError {
|
||||
error_type: LemmyErrorType::DomainNotInAllowList(domain),
|
||||
..
|
||||
} => anyhow!("Domain {domain:?} is not in allowlist"),
|
||||
_ => anyhow!("Failed validating apub id"),
|
||||
} => ActivityPubError::Other(format!("Domain {domain:?} is not in allowlist")),
|
||||
_ => ActivityPubError::Other("Failed validating apub id".into()),
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -183,9 +183,6 @@ impl Object for ApubComment {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
objects::{
|
||||
|
@ -200,46 +197,45 @@ pub(crate) mod tests {
|
|||
use assert_json_diff::assert_json_include;
|
||||
use html2md::parse_html;
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
async fn prepare_comment_test(
|
||||
url: &Url,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> (ApubPerson, ApubCommunity, ApubPost, ApubSite) {
|
||||
) -> LemmyResult<(ApubPerson, ApubCommunity, ApubPost, ApubSite)> {
|
||||
// use separate counter so this doesnt affect tests
|
||||
let context2 = context.reset_request_count();
|
||||
let (person, site) = parse_lemmy_person(&context2).await;
|
||||
let community = parse_lemmy_community(&context2).await;
|
||||
let post_json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
|
||||
ApubPost::verify(&post_json, url, &context2).await.unwrap();
|
||||
let post = ApubPost::from_json(post_json, &context2).await.unwrap();
|
||||
(person, community, post, site)
|
||||
let (person, site) = parse_lemmy_person(&context2).await?;
|
||||
let community = parse_lemmy_community(&context2).await?;
|
||||
let post_json = file_to_json_object("assets/lemmy/objects/page.json")?;
|
||||
ApubPost::verify(&post_json, url, &context2).await?;
|
||||
let post = ApubPost::from_json(post_json, &context2).await?;
|
||||
Ok((person, community, post, site))
|
||||
}
|
||||
|
||||
async fn cleanup(data: (ApubPerson, ApubCommunity, ApubPost, ApubSite), context: &LemmyContext) {
|
||||
Post::delete(&mut context.pool(), data.2.id).await.unwrap();
|
||||
Community::delete(&mut context.pool(), data.1.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Person::delete(&mut context.pool(), data.0.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), data.3.id).await.unwrap();
|
||||
LocalSite::delete(&mut context.pool()).await.unwrap();
|
||||
async fn cleanup(
|
||||
data: (ApubPerson, ApubCommunity, ApubPost, ApubSite),
|
||||
context: &LemmyContext,
|
||||
) -> LemmyResult<()> {
|
||||
Post::delete(&mut context.pool(), data.2.id).await?;
|
||||
Community::delete(&mut context.pool(), data.1.id).await?;
|
||||
Person::delete(&mut context.pool(), data.0.id).await?;
|
||||
Site::delete(&mut context.pool(), data.3.id).await?;
|
||||
LocalSite::delete(&mut context.pool()).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
pub(crate) async fn test_parse_lemmy_comment() {
|
||||
let context = init_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
pub(crate) async fn test_parse_lemmy_comment() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
|
||||
let json: Note = file_to_json_object("assets/lemmy/objects/note.json").unwrap();
|
||||
ApubComment::verify(&json, &url, &context).await.unwrap();
|
||||
let comment = ApubComment::from_json(json.clone(), &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let json: Note = file_to_json_object("assets/lemmy/objects/note.json")?;
|
||||
ApubComment::verify(&json, &url, &context).await?;
|
||||
let comment = ApubComment::from_json(json.clone(), &context).await?;
|
||||
|
||||
assert_eq!(comment.ap_id, url.into());
|
||||
assert_eq!(comment.content.len(), 14);
|
||||
|
@ -247,45 +243,38 @@ pub(crate) mod tests {
|
|||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let comment_id = comment.id;
|
||||
let to_apub = comment.into_json(&context).await.unwrap();
|
||||
let to_apub = comment.into_json(&context).await?;
|
||||
assert_json_include!(actual: json, expected: to_apub);
|
||||
|
||||
Comment::delete(&mut context.pool(), comment_id)
|
||||
.await
|
||||
.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
Comment::delete(&mut context.pool(), comment_id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_pleroma_comment() {
|
||||
let context = init_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741").unwrap();
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
async fn test_parse_pleroma_comment() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/comment/38741")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
|
||||
let pleroma_url =
|
||||
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")
|
||||
.unwrap();
|
||||
let person_json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
ApubPerson::verify(&person_json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
ApubPerson::from_json(person_json, &context).await.unwrap();
|
||||
let json = file_to_json_object("assets/pleroma/objects/note.json").unwrap();
|
||||
ApubComment::verify(&json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let comment = ApubComment::from_json(json, &context).await.unwrap();
|
||||
Url::parse("https://queer.hacktivis.me/objects/8d4973f4-53de-49cd-8c27-df160e16a9c2")?;
|
||||
let person_json = file_to_json_object("assets/pleroma/objects/person.json")?;
|
||||
ApubPerson::verify(&person_json, &pleroma_url, &context).await?;
|
||||
ApubPerson::from_json(person_json, &context).await?;
|
||||
let json = file_to_json_object("assets/pleroma/objects/note.json")?;
|
||||
ApubComment::verify(&json, &pleroma_url, &context).await?;
|
||||
let comment = ApubComment::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(comment.ap_id, pleroma_url.into());
|
||||
assert_eq!(comment.content.len(), 64);
|
||||
assert!(!comment.local);
|
||||
assert_eq!(context.request_count(), 1);
|
||||
|
||||
Comment::delete(&mut context.pool(), comment.id)
|
||||
.await
|
||||
.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
Comment::delete(&mut context.pool(), comment.id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
@ -216,9 +216,6 @@ impl ApubCommunity {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
objects::{instance::tests::parse_lemmy_instance, tests::init_context},
|
||||
|
@ -226,41 +223,44 @@ 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 serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_community(context: &Data<LemmyContext>) -> ApubCommunity {
|
||||
pub(crate) async fn parse_lemmy_community(
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<ApubCommunity> {
|
||||
// use separate counter so this doesnt affect tests
|
||||
let context2 = context.reset_request_count();
|
||||
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json").unwrap();
|
||||
let mut json: Group = file_to_json_object("assets/lemmy/objects/group.json")?;
|
||||
// change these links so they dont fetch over the network
|
||||
json.attributed_to = None;
|
||||
json.outbox =
|
||||
CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox").unwrap();
|
||||
json.followers =
|
||||
CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_followers").unwrap();
|
||||
json.outbox = CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_outbox")?;
|
||||
json.followers = CollectionId::parse("https://enterprise.lemmy.ml/c/tenforward/not_followers")?;
|
||||
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/c/tenforward").unwrap();
|
||||
ApubCommunity::verify(&json, &url, &context2).await.unwrap();
|
||||
let community = ApubCommunity::from_json(json, &context2).await.unwrap();
|
||||
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);
|
||||
community
|
||||
Ok(community)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_community() {
|
||||
let context = init_context().await;
|
||||
let site = parse_lemmy_instance(&context).await;
|
||||
let community = parse_lemmy_community(&context).await;
|
||||
async fn test_parse_lemmy_community() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let site = parse_lemmy_instance(&context).await?;
|
||||
let community = parse_lemmy_community(&context).await?;
|
||||
|
||||
assert_eq!(community.title, "Ten Forward");
|
||||
assert!(!community.local);
|
||||
assert_eq!(community.description.as_ref().unwrap().len(), 132);
|
||||
assert_eq!(
|
||||
community.description.as_ref().map(std::string::String::len),
|
||||
Some(132)
|
||||
);
|
||||
|
||||
Community::delete(&mut context.pool(), community.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), site.id).await.unwrap();
|
||||
Community::delete(&mut context.pool(), community.id).await?;
|
||||
Site::delete(&mut context.pool(), site.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,32 +204,34 @@ pub(in crate::objects) async fn fetch_instance_actor_for_object<T: Into<Url> + C
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{objects::tests::init_context, protocol::tests::file_to_json_object};
|
||||
use lemmy_db_schema::traits::Crud;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> ApubSite {
|
||||
let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
|
||||
let id = Url::parse("https://enterprise.lemmy.ml/").unwrap();
|
||||
ApubSite::verify(&json, &id, context).await.unwrap();
|
||||
let site = ApubSite::from_json(json, context).await.unwrap();
|
||||
pub(crate) async fn parse_lemmy_instance(context: &Data<LemmyContext>) -> LemmyResult<ApubSite> {
|
||||
let json: Instance = file_to_json_object("assets/lemmy/objects/instance.json")?;
|
||||
let id = Url::parse("https://enterprise.lemmy.ml/")?;
|
||||
ApubSite::verify(&json, &id, context).await?;
|
||||
let site = ApubSite::from_json(json, context).await?;
|
||||
assert_eq!(context.request_count(), 0);
|
||||
site
|
||||
Ok(site)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_instance() {
|
||||
let context = init_context().await;
|
||||
let site = parse_lemmy_instance(&context).await;
|
||||
async fn test_parse_lemmy_instance() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let site = parse_lemmy_instance(&context).await?;
|
||||
|
||||
assert_eq!(site.name, "Enterprise");
|
||||
assert_eq!(site.description.as_ref().unwrap().len(), 15);
|
||||
assert_eq!(
|
||||
site.description.as_ref().map(std::string::String::len),
|
||||
Some(15)
|
||||
);
|
||||
|
||||
Site::delete(&mut context.pool(), site.id).await.unwrap();
|
||||
Site::delete(&mut context.pool(), site.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,14 +54,11 @@ pub(crate) fn verify_is_remote_object(id: &Url, settings: &Settings) -> Result<(
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use activitypub_federation::config::{Data, FederationConfig};
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::{context::LemmyContext, request::client_builder};
|
||||
use lemmy_db_schema::{source::secret::Secret, utils::build_db_pool_for_tests};
|
||||
use lemmy_utils::{rate_limit::RateLimitCell, settings::SETTINGS};
|
||||
use lemmy_utils::{error::LemmyResult, rate_limit::RateLimitCell, settings::SETTINGS};
|
||||
use reqwest::{Request, Response};
|
||||
use reqwest_middleware::{ClientBuilder, Middleware, Next};
|
||||
use task_local_extensions::Extensions;
|
||||
|
@ -82,11 +79,11 @@ pub(crate) mod tests {
|
|||
}
|
||||
|
||||
// TODO: would be nice if we didnt have to use a full context for tests.
|
||||
pub(crate) async fn init_context() -> Data<LemmyContext> {
|
||||
pub(crate) async fn init_context() -> LemmyResult<Data<LemmyContext>> {
|
||||
// call this to run migrations
|
||||
let pool = build_db_pool_for_tests().await;
|
||||
|
||||
let client = client_builder(&SETTINGS).build().unwrap();
|
||||
let client = client_builder(&SETTINGS).build()?;
|
||||
|
||||
let client = ClientBuilder::new(client).with(BlockedMiddleware).build();
|
||||
let secret = Secret {
|
||||
|
@ -101,8 +98,7 @@ pub(crate) mod tests {
|
|||
.domain("example.com")
|
||||
.app_data(context)
|
||||
.build()
|
||||
.await
|
||||
.unwrap();
|
||||
config.to_request_data()
|
||||
.await?;
|
||||
Ok(config.to_request_data())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -208,9 +208,6 @@ impl GetActorType for ApubPerson {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
objects::{
|
||||
|
@ -221,60 +218,64 @@ 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 serial_test::serial;
|
||||
|
||||
pub(crate) async fn parse_lemmy_person(context: &Data<LemmyContext>) -> (ApubPerson, ApubSite) {
|
||||
let site = parse_lemmy_instance(context).await;
|
||||
let json = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/u/picard").unwrap();
|
||||
ApubPerson::verify(&json, &url, context).await.unwrap();
|
||||
let person = ApubPerson::from_json(json, context).await.unwrap();
|
||||
pub(crate) async fn parse_lemmy_person(
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<(ApubPerson, ApubSite)> {
|
||||
let site = parse_lemmy_instance(context).await?;
|
||||
let json = file_to_json_object("assets/lemmy/objects/person.json")?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/u/picard")?;
|
||||
ApubPerson::verify(&json, &url, context).await?;
|
||||
let person = ApubPerson::from_json(json, context).await?;
|
||||
assert_eq!(context.request_count(), 0);
|
||||
(person, site)
|
||||
Ok((person, site))
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_person() {
|
||||
let context = init_context().await;
|
||||
let (person, site) = parse_lemmy_person(&context).await;
|
||||
async fn test_parse_lemmy_person() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let (person, site) = parse_lemmy_person(&context).await?;
|
||||
|
||||
assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string()));
|
||||
assert!(!person.local);
|
||||
assert_eq!(person.bio.as_ref().unwrap().len(), 39);
|
||||
assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(39));
|
||||
|
||||
cleanup((person, site), &context).await;
|
||||
cleanup((person, site), &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_pleroma_person() {
|
||||
let context = init_context().await;
|
||||
async fn test_parse_pleroma_person() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
|
||||
// create and parse a fake pleroma instance actor, to avoid network request during test
|
||||
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
|
||||
json.id = ObjectId::parse("https://queer.hacktivis.me/").unwrap();
|
||||
let url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||
ApubSite::verify(&json, &url, &context).await.unwrap();
|
||||
let site = ApubSite::from_json(json, &context).await.unwrap();
|
||||
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json")?;
|
||||
json.id = ObjectId::parse("https://queer.hacktivis.me/")?;
|
||||
let url = Url::parse("https://queer.hacktivis.me/users/lanodan")?;
|
||||
ApubSite::verify(&json, &url, &context).await?;
|
||||
let site = ApubSite::from_json(json, &context).await?;
|
||||
|
||||
let json = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
ApubPerson::verify(&json, &url, &context).await.unwrap();
|
||||
let person = ApubPerson::from_json(json, &context).await.unwrap();
|
||||
let json = file_to_json_object("assets/pleroma/objects/person.json")?;
|
||||
ApubPerson::verify(&json, &url, &context).await?;
|
||||
let person = ApubPerson::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(person.actor_id, url.into());
|
||||
assert_eq!(person.name, "lanodan");
|
||||
assert!(!person.local);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
assert_eq!(person.bio.as_ref().unwrap().len(), 873);
|
||||
assert_eq!(person.bio.as_ref().map(std::string::String::len), Some(873));
|
||||
|
||||
cleanup((person, site), &context).await;
|
||||
cleanup((person, site), &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) {
|
||||
DbPerson::delete(&mut context.pool(), data.0.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), data.1.id).await.unwrap();
|
||||
async fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) -> LemmyResult<()> {
|
||||
DbPerson::delete(&mut context.pool(), data.0.id).await?;
|
||||
Site::delete(&mut context.pool(), data.1.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -292,9 +292,6 @@ impl Object for ApubPost {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
objects::{
|
||||
|
@ -307,44 +304,47 @@ mod tests {
|
|||
protocol::tests::file_to_json_object,
|
||||
};
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_post() {
|
||||
let context = init_context().await;
|
||||
let (person, site) = parse_lemmy_person(&context).await;
|
||||
let community = parse_lemmy_community(&context).await;
|
||||
async fn test_parse_lemmy_post() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let (person, site) = parse_lemmy_person(&context).await?;
|
||||
let community = parse_lemmy_community(&context).await?;
|
||||
|
||||
let json = file_to_json_object("assets/lemmy/objects/page.json").unwrap();
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/post/55143").unwrap();
|
||||
ApubPost::verify(&json, &url, &context).await.unwrap();
|
||||
let post = ApubPost::from_json(json, &context).await.unwrap();
|
||||
let json = file_to_json_object("assets/lemmy/objects/page.json")?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/post/55143")?;
|
||||
ApubPost::verify(&json, &url, &context).await?;
|
||||
let post = ApubPost::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(post.ap_id, url.into());
|
||||
assert_eq!(post.name, "Post title");
|
||||
assert!(post.body.is_some());
|
||||
assert_eq!(post.body.as_ref().unwrap().len(), 45);
|
||||
assert_eq!(post.body.as_ref().map(std::string::String::len), Some(45));
|
||||
assert!(!post.locked);
|
||||
assert!(!post.featured_community);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
cleanup(&context, person, site, community, post).await;
|
||||
cleanup(&context, person, site, community, post).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_convert_mastodon_post_title() {
|
||||
let context = init_context().await;
|
||||
let (person, site) = parse_lemmy_person(&context).await;
|
||||
let community = parse_lemmy_community(&context).await;
|
||||
async fn test_convert_mastodon_post_title() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let (person, site) = parse_lemmy_person(&context).await?;
|
||||
let community = parse_lemmy_community(&context).await?;
|
||||
|
||||
let json = file_to_json_object("assets/mastodon/objects/page.json").unwrap();
|
||||
let post = ApubPost::from_json(json, &context).await.unwrap();
|
||||
let json = file_to_json_object("assets/mastodon/objects/page.json")?;
|
||||
let post = ApubPost::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(post.name, "Variable never resetting at refresh");
|
||||
|
||||
cleanup(&context, person, site, community, post).await;
|
||||
cleanup(&context, person, site, community, post).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn cleanup(
|
||||
|
@ -353,14 +353,11 @@ mod tests {
|
|||
site: ApubSite,
|
||||
community: ApubCommunity,
|
||||
post: ApubPost,
|
||||
) {
|
||||
Post::delete(&mut context.pool(), post.id).await.unwrap();
|
||||
Person::delete(&mut context.pool(), person.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Community::delete(&mut context.pool(), community.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), site.id).await.unwrap();
|
||||
) -> LemmyResult<()> {
|
||||
Post::delete(&mut context.pool(), post.id).await?;
|
||||
Person::delete(&mut context.pool(), person.id).await?;
|
||||
Community::delete(&mut context.pool(), community.id).await?;
|
||||
Site::delete(&mut context.pool(), site.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,9 +141,6 @@ impl Object for ApubPrivateMessage {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use super::*;
|
||||
use crate::{
|
||||
objects::{
|
||||
|
@ -155,90 +152,75 @@ mod tests {
|
|||
};
|
||||
use assert_json_diff::assert_json_include;
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
use serial_test::serial;
|
||||
|
||||
async fn prepare_comment_test(
|
||||
url: &Url,
|
||||
context: &Data<LemmyContext>,
|
||||
) -> (ApubPerson, ApubPerson, ApubSite) {
|
||||
) -> LemmyResult<(ApubPerson, ApubPerson, ApubSite)> {
|
||||
let context2 = context.reset_request_count();
|
||||
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json").unwrap();
|
||||
let site = parse_lemmy_instance(&context2).await;
|
||||
ApubPerson::verify(&lemmy_person, url, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let person1 = ApubPerson::from_json(lemmy_person, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json").unwrap();
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan").unwrap();
|
||||
ApubPerson::verify(&pleroma_person, &pleroma_url, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
let person2 = ApubPerson::from_json(pleroma_person, &context2)
|
||||
.await
|
||||
.unwrap();
|
||||
(person1, person2, site)
|
||||
let lemmy_person = file_to_json_object("assets/lemmy/objects/person.json")?;
|
||||
let site = parse_lemmy_instance(&context2).await?;
|
||||
ApubPerson::verify(&lemmy_person, url, &context2).await?;
|
||||
let person1 = ApubPerson::from_json(lemmy_person, &context2).await?;
|
||||
let pleroma_person = file_to_json_object("assets/pleroma/objects/person.json")?;
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/users/lanodan")?;
|
||||
ApubPerson::verify(&pleroma_person, &pleroma_url, &context2).await?;
|
||||
let person2 = ApubPerson::from_json(pleroma_person, &context2).await?;
|
||||
Ok((person1, person2, site))
|
||||
}
|
||||
|
||||
async fn cleanup(data: (ApubPerson, ApubPerson, ApubSite), context: &Data<LemmyContext>) {
|
||||
Person::delete(&mut context.pool(), data.0.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Person::delete(&mut context.pool(), data.1.id)
|
||||
.await
|
||||
.unwrap();
|
||||
Site::delete(&mut context.pool(), data.2.id).await.unwrap();
|
||||
async fn cleanup(
|
||||
data: (ApubPerson, ApubPerson, ApubSite),
|
||||
context: &Data<LemmyContext>,
|
||||
) -> LemmyResult<()> {
|
||||
Person::delete(&mut context.pool(), data.0.id).await?;
|
||||
Person::delete(&mut context.pool(), data.1.id).await?;
|
||||
Site::delete(&mut context.pool(), data.2.id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_lemmy_pm() {
|
||||
let context = init_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json").unwrap();
|
||||
ApubPrivateMessage::verify(&json, &url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let pm = ApubPrivateMessage::from_json(json.clone(), &context)
|
||||
.await
|
||||
.unwrap();
|
||||
async fn test_parse_lemmy_pm() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
let json: ChatMessage = file_to_json_object("assets/lemmy/objects/chat_message.json")?;
|
||||
ApubPrivateMessage::verify(&json, &url, &context).await?;
|
||||
let pm = ApubPrivateMessage::from_json(json.clone(), &context).await?;
|
||||
|
||||
assert_eq!(pm.ap_id.clone(), url.into());
|
||||
assert_eq!(pm.content.len(), 20);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
let pm_id = pm.id;
|
||||
let to_apub = pm.into_json(&context).await.unwrap();
|
||||
let to_apub = pm.into_json(&context).await?;
|
||||
assert_json_include!(actual: json, expected: to_apub);
|
||||
|
||||
PrivateMessage::delete(&mut context.pool(), pm_id)
|
||||
.await
|
||||
.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
PrivateMessage::delete(&mut context.pool(), pm_id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[serial]
|
||||
async fn test_parse_pleroma_pm() {
|
||||
let context = init_context().await;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621").unwrap();
|
||||
let data = prepare_comment_test(&url, &context).await;
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2").unwrap();
|
||||
let json = file_to_json_object("assets/pleroma/objects/chat_message.json").unwrap();
|
||||
ApubPrivateMessage::verify(&json, &pleroma_url, &context)
|
||||
.await
|
||||
.unwrap();
|
||||
let pm = ApubPrivateMessage::from_json(json, &context).await.unwrap();
|
||||
async fn test_parse_pleroma_pm() -> LemmyResult<()> {
|
||||
let context = init_context().await?;
|
||||
let url = Url::parse("https://enterprise.lemmy.ml/private_message/1621")?;
|
||||
let data = prepare_comment_test(&url, &context).await?;
|
||||
let pleroma_url = Url::parse("https://queer.hacktivis.me/objects/2")?;
|
||||
let json = file_to_json_object("assets/pleroma/objects/chat_message.json")?;
|
||||
ApubPrivateMessage::verify(&json, &pleroma_url, &context).await?;
|
||||
let pm = ApubPrivateMessage::from_json(json, &context).await?;
|
||||
|
||||
assert_eq!(pm.ap_id, pleroma_url.into());
|
||||
assert_eq!(pm.content.len(), 3);
|
||||
assert_eq!(context.request_count(), 0);
|
||||
|
||||
PrivateMessage::delete(&mut context.pool(), pm.id)
|
||||
.await
|
||||
.unwrap();
|
||||
cleanup(data, &context).await;
|
||||
PrivateMessage::delete(&mut context.pool(), pm.id).await?;
|
||||
cleanup(data, &context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,18 +3,16 @@ pub mod undo_block_user;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_block() {
|
||||
test_parse_lemmy_item::<BlockUser>("assets/lemmy/activities/block/block_user.json").unwrap();
|
||||
test_parse_lemmy_item::<UndoBlockUser>("assets/lemmy/activities/block/undo_block_user.json")
|
||||
.unwrap();
|
||||
fn test_parse_lemmy_block() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<BlockUser>("assets/lemmy/activities/block/block_user.json")?;
|
||||
test_parse_lemmy_item::<UndoBlockUser>("assets/lemmy/activities/block/undo_block_user.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@ pub mod update;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::community::{
|
||||
announce::AnnounceActivity,
|
||||
|
@ -21,37 +18,32 @@ mod tests {
|
|||
},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_community_activities() {
|
||||
fn test_parse_lemmy_community_activities() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<AnnounceActivity>(
|
||||
"assets/lemmy/activities/community/announce_create_page.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
test_parse_lemmy_item::<CollectionAdd>("assets/lemmy/activities/community/add_mod.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<CollectionRemove>("assets/lemmy/activities/community/remove_mod.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<CollectionAdd>("assets/lemmy/activities/community/add_mod.json")?;
|
||||
test_parse_lemmy_item::<CollectionRemove>("assets/lemmy/activities/community/remove_mod.json")?;
|
||||
|
||||
test_parse_lemmy_item::<CollectionAdd>(
|
||||
"assets/lemmy/activities/community/add_featured_post.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
test_parse_lemmy_item::<CollectionRemove>(
|
||||
"assets/lemmy/activities/community/remove_featured_post.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
test_parse_lemmy_item::<LockPage>("assets/lemmy/activities/community/lock_page.json").unwrap();
|
||||
test_parse_lemmy_item::<UndoLockPage>("assets/lemmy/activities/community/undo_lock_page.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<LockPage>("assets/lemmy/activities/community/lock_page.json")?;
|
||||
test_parse_lemmy_item::<UndoLockPage>("assets/lemmy/activities/community/undo_lock_page.json")?;
|
||||
|
||||
test_parse_lemmy_item::<UpdateCommunity>(
|
||||
"assets/lemmy/activities/community/update_community.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json").unwrap();
|
||||
test_parse_lemmy_item::<Report>("assets/lemmy/activities/community/report_page.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,6 @@ pub mod page;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::create_or_update::{
|
||||
chat_message::CreateOrUpdateChatMessage,
|
||||
|
@ -15,24 +12,22 @@ mod tests {
|
|||
},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_create_or_update() {
|
||||
fn test_parse_lemmy_create_or_update() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<CreateOrUpdatePage>(
|
||||
"assets/lemmy/activities/create_or_update/create_page.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdatePage>(
|
||||
"assets/lemmy/activities/create_or_update/update_page.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateNote>(
|
||||
"assets/lemmy/activities/create_or_update/create_note.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
test_parse_lemmy_item::<CreateOrUpdateChatMessage>(
|
||||
"assets/lemmy/activities/create_or_update/create_private_message.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,6 +40,9 @@ pub struct Delete {
|
|||
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
||||
/// deleting their own content.
|
||||
pub(crate) summary: Option<String>,
|
||||
/// Nonstandard field, only valid if object refers to a Person. If present, all content from the
|
||||
/// user should be deleted along with the account
|
||||
pub(crate) remove_data: Option<bool>,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
|
@ -52,6 +55,7 @@ impl InCommunity for Delete {
|
|||
post.community_id
|
||||
}
|
||||
DeletableObjects::Post(p) => p.community_id,
|
||||
DeletableObjects::Person(_) => return Err(anyhow!("Person is not part of community").into()),
|
||||
DeletableObjects::PrivateMessage(_) => {
|
||||
return Err(anyhow!("Private message is not part of community").into())
|
||||
}
|
||||
|
|
|
@ -4,31 +4,27 @@ pub mod undo_delete;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::deletion::{delete::Delete, delete_user::DeleteUser, undo_delete::UndoDelete},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_deletion() {
|
||||
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json").unwrap();
|
||||
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json").unwrap();
|
||||
fn test_parse_lemmy_deletion() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/remove_note.json")?;
|
||||
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_page.json")?;
|
||||
|
||||
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_remove_note.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<Delete>("assets/lemmy/activities/deletion/delete_private_message.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_remove_note.json")?;
|
||||
test_parse_lemmy_item::<UndoDelete>("assets/lemmy/activities/deletion/undo_delete_page.json")?;
|
||||
test_parse_lemmy_item::<Delete>(
|
||||
"assets/lemmy/activities/deletion/delete_private_message.json",
|
||||
)?;
|
||||
test_parse_lemmy_item::<UndoDelete>(
|
||||
"assets/lemmy/activities/deletion/undo_delete_private_message.json",
|
||||
)
|
||||
.unwrap();
|
||||
)?;
|
||||
|
||||
test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<DeleteUser>("assets/lemmy/activities/deletion/delete_user.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,19 +4,17 @@ pub mod undo_follow;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::following::{accept::AcceptFollow, follow::Follow, undo_follow::UndoFollow},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_accept_follow() {
|
||||
test_parse_lemmy_item::<Follow>("assets/lemmy/activities/following/follow.json").unwrap();
|
||||
test_parse_lemmy_item::<AcceptFollow>("assets/lemmy/activities/following/accept.json").unwrap();
|
||||
test_parse_lemmy_item::<UndoFollow>("assets/lemmy/activities/following/undo_follow.json")
|
||||
.unwrap();
|
||||
fn test_parse_lemmy_accept_follow() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<Follow>("assets/lemmy/activities/following/follow.json")?;
|
||||
test_parse_lemmy_item::<AcceptFollow>("assets/lemmy/activities/following/accept.json")?;
|
||||
test_parse_lemmy_item::<UndoFollow>("assets/lemmy/activities/following/undo_follow.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,9 +16,6 @@ pub enum CreateOrUpdateType {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::{
|
||||
community::announce::AnnounceActivity,
|
||||
|
@ -29,58 +26,66 @@ mod tests {
|
|||
},
|
||||
tests::test_json,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_smithereen_activities() {
|
||||
test_json::<CreateOrUpdateNote>("assets/smithereen/activities/create_note.json").unwrap();
|
||||
fn test_parse_smithereen_activities() -> LemmyResult<()> {
|
||||
test_json::<CreateOrUpdateNote>("assets/smithereen/activities/create_note.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_pleroma_activities() {
|
||||
test_json::<CreateOrUpdateNote>("assets/pleroma/activities/create_note.json").unwrap();
|
||||
test_json::<Delete>("assets/pleroma/activities/delete.json").unwrap();
|
||||
test_json::<Follow>("assets/pleroma/activities/follow.json").unwrap();
|
||||
fn test_parse_pleroma_activities() -> LemmyResult<()> {
|
||||
test_json::<CreateOrUpdateNote>("assets/pleroma/activities/create_note.json")?;
|
||||
test_json::<Delete>("assets/pleroma/activities/delete.json")?;
|
||||
test_json::<Follow>("assets/pleroma/activities/follow.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mastodon_activities() {
|
||||
test_json::<CreateOrUpdateNote>("assets/mastodon/activities/create_note.json").unwrap();
|
||||
test_json::<Delete>("assets/mastodon/activities/delete.json").unwrap();
|
||||
test_json::<Follow>("assets/mastodon/activities/follow.json").unwrap();
|
||||
test_json::<UndoFollow>("assets/mastodon/activities/undo_follow.json").unwrap();
|
||||
test_json::<Vote>("assets/mastodon/activities/like_page.json").unwrap();
|
||||
test_json::<UndoVote>("assets/mastodon/activities/undo_like_page.json").unwrap();
|
||||
fn test_parse_mastodon_activities() -> LemmyResult<()> {
|
||||
test_json::<CreateOrUpdateNote>("assets/mastodon/activities/create_note.json")?;
|
||||
test_json::<Delete>("assets/mastodon/activities/delete.json")?;
|
||||
test_json::<Follow>("assets/mastodon/activities/follow.json")?;
|
||||
test_json::<UndoFollow>("assets/mastodon/activities/undo_follow.json")?;
|
||||
test_json::<Vote>("assets/mastodon/activities/like_page.json")?;
|
||||
test_json::<UndoVote>("assets/mastodon/activities/undo_like_page.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_lotide_activities() {
|
||||
test_json::<Follow>("assets/lotide/activities/follow.json").unwrap();
|
||||
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page.json").unwrap();
|
||||
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page_image.json").unwrap();
|
||||
test_json::<CreateOrUpdateNote>("assets/lotide/activities/create_note_reply.json").unwrap();
|
||||
fn test_parse_lotide_activities() -> LemmyResult<()> {
|
||||
test_json::<Follow>("assets/lotide/activities/follow.json")?;
|
||||
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page.json")?;
|
||||
test_json::<CreateOrUpdatePage>("assets/lotide/activities/create_page_image.json")?;
|
||||
test_json::<CreateOrUpdateNote>("assets/lotide/activities/create_note_reply.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_friendica_activities() {
|
||||
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_1.json").unwrap();
|
||||
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_2.json").unwrap();
|
||||
test_json::<CreateOrUpdateNote>("assets/friendica/activities/create_note.json").unwrap();
|
||||
test_json::<CreateOrUpdateNote>("assets/friendica/activities/update_note.json").unwrap();
|
||||
test_json::<Delete>("assets/friendica/activities/delete.json").unwrap();
|
||||
test_json::<Vote>("assets/friendica/activities/like_page.json").unwrap();
|
||||
test_json::<Vote>("assets/friendica/activities/dislike_page.json").unwrap();
|
||||
test_json::<UndoVote>("assets/friendica/activities/undo_dislike_page.json").unwrap();
|
||||
fn test_parse_friendica_activities() -> LemmyResult<()> {
|
||||
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_1.json")?;
|
||||
test_json::<CreateOrUpdatePage>("assets/friendica/activities/create_page_2.json")?;
|
||||
test_json::<CreateOrUpdateNote>("assets/friendica/activities/create_note.json")?;
|
||||
test_json::<CreateOrUpdateNote>("assets/friendica/activities/update_note.json")?;
|
||||
test_json::<Delete>("assets/friendica/activities/delete.json")?;
|
||||
test_json::<Vote>("assets/friendica/activities/like_page.json")?;
|
||||
test_json::<Vote>("assets/friendica/activities/dislike_page.json")?;
|
||||
test_json::<UndoVote>("assets/friendica/activities/undo_dislike_page.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_gnusocial_activities() {
|
||||
test_json::<CreateOrUpdatePage>("assets/gnusocial/activities/create_page.json").unwrap();
|
||||
test_json::<CreateOrUpdateNote>("assets/gnusocial/activities/create_note.json").unwrap();
|
||||
test_json::<Vote>("assets/gnusocial/activities/like_note.json").unwrap();
|
||||
fn test_parse_gnusocial_activities() -> LemmyResult<()> {
|
||||
test_json::<CreateOrUpdatePage>("assets/gnusocial/activities/create_page.json")?;
|
||||
test_json::<CreateOrUpdateNote>("assets/gnusocial/activities/create_note.json")?;
|
||||
test_json::<Vote>("assets/gnusocial/activities/like_note.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_peertube_activities() {
|
||||
test_json::<AnnounceActivity>("assets/peertube/activities/announce_video.json").unwrap();
|
||||
fn test_parse_peertube_activities() -> LemmyResult<()> {
|
||||
test_json::<AnnounceActivity>("assets/peertube/activities/announce_video.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,22 +3,19 @@ pub mod vote;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
activities::voting::{undo_vote::UndoVote, vote::Vote},
|
||||
tests::test_parse_lemmy_item,
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_voting() {
|
||||
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/like_note.json").unwrap();
|
||||
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/dislike_page.json").unwrap();
|
||||
fn test_parse_lemmy_voting() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/like_note.json")?;
|
||||
test_parse_lemmy_item::<Vote>("assets/lemmy/activities/voting/dislike_page.json")?;
|
||||
|
||||
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_like_note.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_dislike_page.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_like_note.json")?;
|
||||
test_parse_lemmy_item::<UndoVote>("assets/lemmy/activities/voting/undo_dislike_page.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,9 +6,6 @@ pub(crate) mod group_outbox;
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
collections::{
|
||||
empty_outbox::EmptyOutbox,
|
||||
|
@ -19,23 +16,23 @@ mod tests {
|
|||
},
|
||||
tests::{test_json, test_parse_lemmy_item},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_lemmy_collections() {
|
||||
test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json")
|
||||
.unwrap();
|
||||
fn test_parse_lemmy_collections() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<GroupFollowers>("assets/lemmy/collections/group_followers.json")?;
|
||||
let outbox =
|
||||
test_parse_lemmy_item::<GroupOutbox>("assets/lemmy/collections/group_outbox.json").unwrap();
|
||||
test_parse_lemmy_item::<GroupOutbox>("assets/lemmy/collections/group_outbox.json")?;
|
||||
assert_eq!(outbox.ordered_items.len() as i32, outbox.total_items);
|
||||
test_parse_lemmy_item::<GroupFeatured>("assets/lemmy/collections/group_featured_posts.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<GroupModerators>("assets/lemmy/collections/group_moderators.json")
|
||||
.unwrap();
|
||||
test_parse_lemmy_item::<EmptyOutbox>("assets/lemmy/collections/person_outbox.json").unwrap();
|
||||
test_parse_lemmy_item::<GroupFeatured>("assets/lemmy/collections/group_featured_posts.json")?;
|
||||
test_parse_lemmy_item::<GroupModerators>("assets/lemmy/collections/group_moderators.json")?;
|
||||
test_parse_lemmy_item::<EmptyOutbox>("assets/lemmy/collections/person_outbox.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_mastodon_collections() {
|
||||
test_json::<GroupFeatured>("assets/mastodon/collections/featured.json").unwrap();
|
||||
fn test_parse_mastodon_collections() -> LemmyResult<()> {
|
||||
test_json::<GroupFeatured>("assets/mastodon/collections/featured.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -89,9 +89,6 @@ pub trait InCommunity {
|
|||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use activitypub_federation::protocol::context::WithContext;
|
||||
use assert_json_diff::assert_json_include;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
|
|
@ -95,9 +95,6 @@ impl LanguageTag {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{
|
||||
objects::{
|
||||
chat_message::ChatMessage,
|
||||
|
@ -110,77 +107,87 @@ mod tests {
|
|||
},
|
||||
tests::{test_json, test_parse_lemmy_item},
|
||||
};
|
||||
use lemmy_utils::error::LemmyResult;
|
||||
|
||||
#[test]
|
||||
fn test_parse_objects_lemmy() {
|
||||
test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json").unwrap();
|
||||
test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json").unwrap();
|
||||
test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json").unwrap();
|
||||
test_parse_lemmy_item::<Page>("assets/lemmy/objects/page.json").unwrap();
|
||||
test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json").unwrap();
|
||||
test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json").unwrap();
|
||||
test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json").unwrap();
|
||||
fn test_parse_objects_lemmy() -> LemmyResult<()> {
|
||||
test_parse_lemmy_item::<Instance>("assets/lemmy/objects/instance.json")?;
|
||||
test_parse_lemmy_item::<Group>("assets/lemmy/objects/group.json")?;
|
||||
test_parse_lemmy_item::<Person>("assets/lemmy/objects/person.json")?;
|
||||
test_parse_lemmy_item::<Page>("assets/lemmy/objects/page.json")?;
|
||||
test_parse_lemmy_item::<Note>("assets/lemmy/objects/note.json")?;
|
||||
test_parse_lemmy_item::<ChatMessage>("assets/lemmy/objects/chat_message.json")?;
|
||||
test_parse_lemmy_item::<Tombstone>("assets/lemmy/objects/tombstone.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_objects_pleroma() {
|
||||
test_json::<Person>("assets/pleroma/objects/person.json").unwrap();
|
||||
test_json::<Note>("assets/pleroma/objects/note.json").unwrap();
|
||||
test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json").unwrap();
|
||||
fn test_parse_objects_pleroma() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/pleroma/objects/person.json")?;
|
||||
test_json::<Note>("assets/pleroma/objects/note.json")?;
|
||||
test_json::<ChatMessage>("assets/pleroma/objects/chat_message.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_objects_smithereen() {
|
||||
test_json::<Person>("assets/smithereen/objects/person.json").unwrap();
|
||||
test_json::<Note>("assets/smithereen/objects/note.json").unwrap();
|
||||
fn test_parse_objects_smithereen() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/smithereen/objects/person.json")?;
|
||||
test_json::<Note>("assets/smithereen/objects/note.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_objects_mastodon() {
|
||||
test_json::<Person>("assets/mastodon/objects/person.json").unwrap();
|
||||
test_json::<Note>("assets/mastodon/objects/note.json").unwrap();
|
||||
test_json::<Page>("assets/mastodon/objects/page.json").unwrap();
|
||||
fn test_parse_objects_mastodon() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/mastodon/objects/person.json")?;
|
||||
test_json::<Note>("assets/mastodon/objects/note.json")?;
|
||||
test_json::<Page>("assets/mastodon/objects/page.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_objects_lotide() {
|
||||
test_json::<Group>("assets/lotide/objects/group.json").unwrap();
|
||||
test_json::<Person>("assets/lotide/objects/person.json").unwrap();
|
||||
test_json::<Note>("assets/lotide/objects/note.json").unwrap();
|
||||
test_json::<Page>("assets/lotide/objects/page.json").unwrap();
|
||||
test_json::<Tombstone>("assets/lotide/objects/tombstone.json").unwrap();
|
||||
fn test_parse_objects_lotide() -> LemmyResult<()> {
|
||||
test_json::<Group>("assets/lotide/objects/group.json")?;
|
||||
test_json::<Person>("assets/lotide/objects/person.json")?;
|
||||
test_json::<Note>("assets/lotide/objects/note.json")?;
|
||||
test_json::<Page>("assets/lotide/objects/page.json")?;
|
||||
test_json::<Tombstone>("assets/lotide/objects/tombstone.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_object_friendica() {
|
||||
test_json::<Person>("assets/friendica/objects/person_1.json").unwrap();
|
||||
test_json::<Person>("assets/friendica/objects/person_2.json").unwrap();
|
||||
test_json::<Page>("assets/friendica/objects/page_1.json").unwrap();
|
||||
test_json::<Page>("assets/friendica/objects/page_2.json").unwrap();
|
||||
test_json::<Note>("assets/friendica/objects/note_1.json").unwrap();
|
||||
test_json::<Note>("assets/friendica/objects/note_2.json").unwrap();
|
||||
fn test_parse_object_friendica() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/friendica/objects/person_1.json")?;
|
||||
test_json::<Person>("assets/friendica/objects/person_2.json")?;
|
||||
test_json::<Page>("assets/friendica/objects/page_1.json")?;
|
||||
test_json::<Page>("assets/friendica/objects/page_2.json")?;
|
||||
test_json::<Note>("assets/friendica/objects/note_1.json")?;
|
||||
test_json::<Note>("assets/friendica/objects/note_2.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_object_gnusocial() {
|
||||
test_json::<Person>("assets/gnusocial/objects/person.json").unwrap();
|
||||
test_json::<Group>("assets/gnusocial/objects/group.json").unwrap();
|
||||
test_json::<Page>("assets/gnusocial/objects/page.json").unwrap();
|
||||
test_json::<Note>("assets/gnusocial/objects/note.json").unwrap();
|
||||
fn test_parse_object_gnusocial() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/gnusocial/objects/person.json")?;
|
||||
test_json::<Group>("assets/gnusocial/objects/group.json")?;
|
||||
test_json::<Page>("assets/gnusocial/objects/page.json")?;
|
||||
test_json::<Note>("assets/gnusocial/objects/note.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_object_peertube() {
|
||||
test_json::<Person>("assets/peertube/objects/person.json").unwrap();
|
||||
test_json::<Group>("assets/peertube/objects/group.json").unwrap();
|
||||
test_json::<Page>("assets/peertube/objects/video.json").unwrap();
|
||||
test_json::<Note>("assets/peertube/objects/note.json").unwrap();
|
||||
fn test_parse_object_peertube() -> LemmyResult<()> {
|
||||
test_json::<Person>("assets/peertube/objects/person.json")?;
|
||||
test_json::<Group>("assets/peertube/objects/group.json")?;
|
||||
test_json::<Page>("assets/peertube/objects/video.json")?;
|
||||
test_json::<Note>("assets/peertube/objects/note.json")?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_object_mobilizon() {
|
||||
test_json::<Group>("assets/mobilizon/objects/group.json").unwrap();
|
||||
test_json::<Page>("assets/mobilizon/objects/event.json").unwrap();
|
||||
test_json::<Person>("assets/mobilizon/objects/person.json").unwrap();
|
||||
fn test_parse_object_mobilizon() -> LemmyResult<()> {
|
||||
test_json::<Group>("assets/mobilizon/objects/group.json")?;
|
||||
test_json::<Page>("assets/mobilizon/objects/event.json")?;
|
||||
test_json::<Person>("assets/mobilizon/objects/person.json")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -242,9 +242,6 @@ where
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#![allow(clippy::unwrap_used)]
|
||||
#![allow(clippy::indexing_slicing)]
|
||||
|
||||
use crate::protocol::{objects::page::Page, tests::test_parse_lemmy_item};
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -13,6 +13,9 @@ name = "lemmy_db_schema"
|
|||
path = "src/lib.rs"
|
||||
doctest = false
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[features]
|
||||
full = [
|
||||
"diesel",
|
||||
|
|
|
@ -13,10 +13,14 @@ use serde::{Deserialize, Serialize};
|
|||
#[cfg(feature = "full")]
|
||||
use ts_rs::TS;
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment_aggregates))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(comment_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Aggregate data for a comment.
|
||||
pub struct CommentAggregates {
|
||||
|
@ -34,7 +38,10 @@ pub struct CommentAggregates {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_aggregates))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
|
@ -62,10 +69,14 @@ pub struct CommunityAggregates {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Default)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person_aggregates))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Aggregate data for a person.
|
||||
pub struct PersonAggregates {
|
||||
|
@ -79,10 +90,14 @@ pub struct PersonAggregates {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_aggregates))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(post_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Aggregate data for a post.
|
||||
pub struct PostAggregates {
|
||||
|
@ -122,10 +137,14 @@ pub struct PostAggregates {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person_post_aggregates))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// Aggregate data for a person's post.
|
||||
pub struct PersonPostAggregates {
|
||||
pub person_id: PersonId,
|
||||
|
@ -148,10 +167,14 @@ pub struct PersonPostAggregatesForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = site_aggregates))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(site_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Aggregate data for a site.
|
||||
pub struct SiteAggregates {
|
||||
|
|
|
@ -76,6 +76,8 @@ impl LocalUser {
|
|||
community,
|
||||
community_block,
|
||||
community_follower,
|
||||
instance,
|
||||
instance_block,
|
||||
person,
|
||||
person_block,
|
||||
post,
|
||||
|
@ -118,6 +120,13 @@ impl LocalUser {
|
|||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
let blocked_instances = instance_block::dsl::instance_block
|
||||
.filter(instance_block::person_id.eq(person_id_))
|
||||
.inner_join(instance::table)
|
||||
.select(instance::domain)
|
||||
.get_results(conn)
|
||||
.await?;
|
||||
|
||||
// TODO: use join for parallel queries?
|
||||
|
||||
Ok(UserBackupLists {
|
||||
|
@ -126,6 +135,7 @@ impl LocalUser {
|
|||
saved_comments,
|
||||
blocked_communities,
|
||||
blocked_users,
|
||||
blocked_instances,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -136,6 +146,7 @@ pub struct UserBackupLists {
|
|||
pub saved_comments: Vec<DbUrl>,
|
||||
pub blocked_communities: Vec<DbUrl>,
|
||||
pub blocked_users: Vec<DbUrl>,
|
||||
pub blocked_instances: Vec<String>,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use crate::{
|
||||
newtypes::{ActivityId, CommunityId, DbUrl},
|
||||
schema::sent_activity,
|
||||
schema::{received_activity, sent_activity},
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use diesel::{sql_types::Nullable, Queryable};
|
||||
|
@ -51,8 +51,10 @@ impl ActivitySendTargets {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Queryable)]
|
||||
#[diesel(table_name = sent_activity)]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = sent_activity))]
|
||||
pub struct SentActivity {
|
||||
pub id: ActivityId,
|
||||
pub ap_id: DbUrl,
|
||||
|
@ -66,8 +68,8 @@ pub struct SentActivity {
|
|||
pub actor_apub_id: Option<DbUrl>,
|
||||
}
|
||||
|
||||
#[derive(Insertable)]
|
||||
#[diesel(table_name = sent_activity)]
|
||||
#[cfg_attr(feature = "full", derive(Insertable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = sent_activity))]
|
||||
pub struct SentActivityForm {
|
||||
pub ap_id: DbUrl,
|
||||
pub data: Value,
|
||||
|
@ -87,10 +89,12 @@ pub enum ActorType {
|
|||
Person,
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Queryable)]
|
||||
#[diesel(table_name = received_activity)]
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(ap_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = received_activity))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct ReceivedActivity {
|
||||
pub id: i64,
|
||||
pub ap_id: DbUrl,
|
||||
pub published: DateTime<Utc>,
|
||||
}
|
||||
|
|
|
@ -4,9 +4,10 @@ use crate::schema::local_user_language;
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_user_language))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(local_user_id, language_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct LocalUserLanguage {
|
||||
pub local_user_id: LocalUserId,
|
||||
pub language_id: LanguageId,
|
||||
|
@ -24,9 +25,10 @@ pub struct LocalUserLanguageForm {
|
|||
use crate::schema::community_language;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_language))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(community_id, language_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommunityLanguage {
|
||||
pub community_id: CommunityId,
|
||||
pub language_id: LanguageId,
|
||||
|
@ -44,9 +46,10 @@ pub struct CommunityLanguageForm {
|
|||
use crate::schema::site_language;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = site_language))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(site_id, language_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct SiteLanguage {
|
||||
pub site_id: SiteId,
|
||||
pub language_id: LanguageId,
|
||||
|
|
|
@ -7,8 +7,9 @@ use uuid::Uuid;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = captcha_answer))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CaptchaAnswer {
|
||||
pub uuid: Uuid,
|
||||
pub answer: String,
|
||||
|
@ -17,8 +18,9 @@ pub struct CaptchaAnswer {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = captcha_answer))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CheckCaptchaAnswer {
|
||||
pub uuid: Uuid,
|
||||
pub answer: String,
|
||||
|
|
|
@ -14,10 +14,14 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// A comment.
|
||||
pub struct Comment {
|
||||
pub id: CommentId,
|
||||
|
@ -83,10 +87,14 @@ pub struct CommentUpdateForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment_like))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommentLike {
|
||||
pub person_id: PersonId,
|
||||
pub comment_id: CommentId,
|
||||
|
@ -106,10 +114,14 @@ pub struct CommentLikeForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment_saved))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, comment_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommentSaved {
|
||||
pub comment_id: CommentId,
|
||||
pub person_id: PersonId,
|
||||
|
|
|
@ -7,9 +7,13 @@ use serde::{Deserialize, Serialize};
|
|||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment_reply))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A comment reply.
|
||||
pub struct CommentReply {
|
||||
|
|
|
@ -9,9 +9,13 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = comment_report))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A comment report.
|
||||
pub struct CommentReport {
|
||||
|
|
|
@ -13,8 +13,9 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A community.
|
||||
pub struct Community {
|
||||
|
@ -128,13 +129,17 @@ pub struct CommunityUpdateForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::community::Community))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_moderator))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommunityModerator {
|
||||
pub community_id: CommunityId,
|
||||
pub person_id: PersonId,
|
||||
|
@ -150,13 +155,17 @@ pub struct CommunityModeratorForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::community::Community))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_person_ban))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommunityPersonBan {
|
||||
pub community_id: CommunityId,
|
||||
pub person_id: PersonId,
|
||||
|
@ -174,13 +183,17 @@ pub struct CommunityPersonBanForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::community::Community))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_follower))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommunityFollower {
|
||||
pub community_id: CommunityId,
|
||||
pub person_id: PersonId,
|
||||
|
|
|
@ -5,13 +5,17 @@ use chrono::{DateTime, Utc};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::community::Community))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = community_block))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, community_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct CommunityBlock {
|
||||
pub person_id: PersonId,
|
||||
pub community_id: CommunityId,
|
||||
|
|
|
@ -10,12 +10,16 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::local_site::LocalSite))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A custom emoji.
|
||||
pub struct CustomEmoji {
|
||||
|
|
|
@ -7,13 +7,17 @@ use ts_rs::TS;
|
|||
use typed_builder::TypedBuilder;
|
||||
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = custom_emoji_keyword))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::custom_emoji::CustomEmoji))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(custom_emoji_id, keyword)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A custom keyword for an emoji.
|
||||
pub struct CustomEmojiKeyword {
|
||||
|
|
|
@ -4,13 +4,14 @@ use crate::schema::email_verification;
|
|||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = email_verification))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct EmailVerification {
|
||||
pub id: i32,
|
||||
pub local_user_id: LocalUserId,
|
||||
pub email: String,
|
||||
pub verification_code: String,
|
||||
pub verification_token: String,
|
||||
pub published: DateTime<Utc>,
|
||||
}
|
||||
|
||||
|
|
|
@ -6,13 +6,17 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::instance::Instance))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct FederationAllowList {
|
||||
pub instance_id: InstanceId,
|
||||
pub published: DateTime<Utc>,
|
||||
|
|
|
@ -6,13 +6,17 @@ use serde::{Deserialize, Serialize};
|
|||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::instance::Instance))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(instance_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct FederationBlockList {
|
||||
pub instance_id: InstanceId,
|
||||
pub published: DateTime<Utc>,
|
||||
|
|
|
@ -11,7 +11,10 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = image_upload))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(pictrs_alias)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
|
@ -19,6 +22,7 @@ use typed_builder::TypedBuilder;
|
|||
feature = "full",
|
||||
diesel(belongs_to(crate::source::local_user::LocalUser))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct ImageUpload {
|
||||
pub local_user_id: LocalUserId,
|
||||
pub pictrs_alias: String,
|
||||
|
|
|
@ -13,6 +13,7 @@ use typed_builder::TypedBuilder;
|
|||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = instance))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A federated instance / site.
|
||||
pub struct Instance {
|
||||
|
|
|
@ -5,13 +5,17 @@ use chrono::{DateTime, Utc};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::instance::Instance))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = instance_block))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, instance_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct InstanceBlock {
|
||||
pub person_id: PersonId,
|
||||
pub instance_id: InstanceId,
|
||||
|
|
|
@ -6,8 +6,9 @@ use serde::{Deserialize, Serialize};
|
|||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = language))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A language.
|
||||
pub struct Language {
|
||||
|
|
|
@ -17,6 +17,7 @@ use typed_builder::TypedBuilder;
|
|||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_site))]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The local site.
|
||||
pub struct LocalSite {
|
||||
|
|
|
@ -10,13 +10,14 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(local_site_id)))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::local_site::LocalSite))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// Rate limits for your site. Given in count / length of time.
|
||||
pub struct LocalSiteRateLimit {
|
||||
|
|
|
@ -14,8 +14,9 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_user))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A local user.
|
||||
pub struct LocalUser {
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
use crate::newtypes::{LanguageId, LocalUserId, LocalUserLanguageId};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[cfg(feature = "full")]
|
||||
use crate::schema::local_user_language;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = local_user_language))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct LocalUserLanguage {
|
||||
#[serde(skip)]
|
||||
pub id: LocalUserLanguageId,
|
||||
|
|
|
@ -10,9 +10,10 @@ use ts_rs::TS;
|
|||
/// Stores data related to a specific user login session.
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = login_token))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(token)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
pub struct LoginToken {
|
||||
/// Jwt token for this login
|
||||
|
|
|
@ -25,8 +25,9 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_remove_post))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator removes a post.
|
||||
pub struct ModRemovePost {
|
||||
|
@ -48,8 +49,9 @@ pub struct ModRemovePostForm {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_lock_post))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator locks a post (prevents new comments being made).
|
||||
pub struct ModLockPost {
|
||||
|
@ -69,8 +71,9 @@ pub struct ModLockPostForm {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_feature_post))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator features a post on a community (pins it to the top).
|
||||
pub struct ModFeaturePost {
|
||||
|
@ -93,8 +96,9 @@ pub struct ModFeaturePostForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_remove_comment))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator removes a comment.
|
||||
pub struct ModRemoveComment {
|
||||
|
@ -117,8 +121,9 @@ pub struct ModRemoveCommentForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_remove_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator removes a community.
|
||||
pub struct ModRemoveCommunity {
|
||||
|
@ -141,8 +146,9 @@ pub struct ModRemoveCommunityForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_ban_from_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When someone is banned from a community.
|
||||
pub struct ModBanFromCommunity {
|
||||
|
@ -169,8 +175,9 @@ pub struct ModBanFromCommunityForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_ban))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When someone is banned from the site.
|
||||
pub struct ModBan {
|
||||
|
@ -194,8 +201,9 @@ pub struct ModHideCommunityForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_hide_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a community is hidden from public view.
|
||||
pub struct ModHideCommunity {
|
||||
|
@ -218,8 +226,9 @@ pub struct ModBanForm {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_add_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When someone is added as a community moderator.
|
||||
pub struct ModAddCommunity {
|
||||
|
@ -241,8 +250,9 @@ pub struct ModAddCommunityForm {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_transfer_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When a moderator transfers a community to a new owner.
|
||||
pub struct ModTransferCommunity {
|
||||
|
@ -262,8 +272,9 @@ pub struct ModTransferCommunityForm {
|
|||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = mod_add))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When someone is added as a site moderator.
|
||||
pub struct ModAdd {
|
||||
|
@ -284,8 +295,9 @@ pub struct ModAddForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = admin_purge_person))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When an admin purges a person.
|
||||
pub struct AdminPurgePerson {
|
||||
|
@ -304,8 +316,9 @@ pub struct AdminPurgePersonForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = admin_purge_community))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When an admin purges a community.
|
||||
pub struct AdminPurgeCommunity {
|
||||
|
@ -324,8 +337,9 @@ pub struct AdminPurgeCommunityForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = admin_purge_post))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When an admin purges a post.
|
||||
pub struct AdminPurgePost {
|
||||
|
@ -346,8 +360,9 @@ pub struct AdminPurgePostForm {
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = admin_purge_comment))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// When an admin purges a comment.
|
||||
pub struct AdminPurgeComment {
|
||||
|
|
|
@ -4,8 +4,9 @@ use crate::schema::password_reset_request;
|
|||
use chrono::{DateTime, Utc};
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PasswordResetRequest {
|
||||
pub id: i32,
|
||||
pub token: String,
|
||||
|
|
|
@ -13,8 +13,9 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A person.
|
||||
pub struct Person {
|
||||
|
@ -112,10 +113,14 @@ pub struct PersonUpdateForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person_follower))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(follower_id, person_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PersonFollower {
|
||||
pub person_id: PersonId,
|
||||
pub follower_id: PersonId,
|
||||
|
|
|
@ -5,10 +5,14 @@ use chrono::{DateTime, Utc};
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::person::Person)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person_block))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, target_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PersonBlock {
|
||||
pub person_id: PersonId,
|
||||
pub target_id: PersonId,
|
||||
|
|
|
@ -7,9 +7,13 @@ use serde::{Deserialize, Serialize};
|
|||
use ts_rs::TS;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = person_mention))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A person mention.
|
||||
pub struct PersonMention {
|
||||
|
|
|
@ -10,9 +10,10 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
/// A post.
|
||||
pub struct Post {
|
||||
pub id: PostId,
|
||||
|
@ -111,10 +112,14 @@ pub struct PostUpdateForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_like))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(person_id, post_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PostLike {
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
|
@ -132,10 +137,14 @@ pub struct PostLikeForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_saved))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(post_id, person_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PostSaved {
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
|
@ -150,11 +159,15 @@ pub struct PostSavedForm {
|
|||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_read))]
|
||||
#[cfg_attr(feature = "full", diesel(primary_key(post_id, person_id)))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
pub struct PostRead {
|
||||
pub id: i32,
|
||||
pub post_id: PostId,
|
||||
pub person_id: PersonId,
|
||||
pub published: DateTime<Utc>,
|
||||
|
|
|
@ -9,9 +9,13 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Identifiable, Queryable, Selectable, Associations, TS)
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::post::Post)))] // Is this the right assoc?
|
||||
#[cfg_attr(feature = "full", diesel(table_name = post_report))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A post report.
|
||||
pub struct PostReport {
|
||||
|
|
|
@ -10,12 +10,16 @@ use typed_builder::TypedBuilder;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::person::Person, foreign_key = creator_id)
|
||||
))] // Is this the right assoc?
|
||||
#[cfg_attr(feature = "full", diesel(table_name = private_message))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A private message.
|
||||
pub struct PrivateMessage {
|
||||
|
|
|
@ -9,12 +9,16 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable, TS))]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
derive(Queryable, Selectable, Associations, Identifiable, TS)
|
||||
)]
|
||||
#[cfg_attr(
|
||||
feature = "full",
|
||||
diesel(belongs_to(crate::source::private_message::PrivateMessage))
|
||||
)]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = private_message_report))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// The private message report.
|
||||
pub struct PrivateMessageReport {
|
||||
|
|
|
@ -9,8 +9,9 @@ use ts_rs::TS;
|
|||
|
||||
#[skip_serializing_none]
|
||||
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", derive(Queryable, Selectable, Identifiable, TS))]
|
||||
#[cfg_attr(feature = "full", diesel(table_name = registration_application))]
|
||||
#[cfg_attr(feature = "full", diesel(check_for_backend(diesel::pg::Pg)))]
|
||||
#[cfg_attr(feature = "full", ts(export))]
|
||||
/// A registration application.
|
||||
pub struct RegistrationApplication {
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue