mirror of
https://github.com/LemmyNet/lemmy.git
synced 2025-01-11 12:35:54 +00:00
Merge remote-tracking branch 'origin/main' into persistent-queue
This commit is contained in:
commit
2d3ad1b076
427 changed files with 25309 additions and 15179 deletions
|
@ -3,6 +3,22 @@
|
||||||
|
|
||||||
variables:
|
variables:
|
||||||
- &muslrust_image "clux/muslrust:1.70.0"
|
- &muslrust_image "clux/muslrust:1.70.0"
|
||||||
|
- &slow_check_paths
|
||||||
|
- path:
|
||||||
|
# rust source code
|
||||||
|
- "**/*.rs"
|
||||||
|
- "**/Cargo.toml"
|
||||||
|
- "Cargo.lock"
|
||||||
|
# database migrations
|
||||||
|
- "migrations"
|
||||||
|
# typescript tests
|
||||||
|
- "api_tests"
|
||||||
|
# config files and scripts used by ci
|
||||||
|
- ".woodpecker.yml"
|
||||||
|
- ".rustfmt.toml"
|
||||||
|
- "scripts/update_config_defaults.sh"
|
||||||
|
- "diesel.toml"
|
||||||
|
- ".gitmodules"
|
||||||
|
|
||||||
# Broken for cron jobs currently, see
|
# Broken for cron jobs currently, see
|
||||||
# https://github.com/woodpecker-ci/woodpecker/issues/1716
|
# https://github.com/woodpecker-ci/woodpecker/issues/1716
|
||||||
|
@ -48,6 +64,7 @@ pipeline:
|
||||||
- "api_tests/node_modules"
|
- "api_tests/node_modules"
|
||||||
secrets:
|
secrets:
|
||||||
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
toml_fmt:
|
toml_fmt:
|
||||||
image: tamasfe/taplo:0.8.1
|
image: tamasfe/taplo:0.8.1
|
||||||
|
@ -65,8 +82,18 @@ pipeline:
|
||||||
- rustup toolchain install nightly-2023-07-10
|
- rustup toolchain install nightly-2023-07-10
|
||||||
- rustup component add rustfmt --toolchain nightly-2023-07-10
|
- rustup component add rustfmt --toolchain nightly-2023-07-10
|
||||||
- cargo +nightly-2023-07-10 fmt -- --check
|
- cargo +nightly-2023-07-10 fmt -- --check
|
||||||
# when:
|
|
||||||
# platform: linux/amd64
|
sql_fmt:
|
||||||
|
image: alpine:3
|
||||||
|
commands:
|
||||||
|
- apk add bash wget perl make git
|
||||||
|
- wget https://github.com/darold/pgFormatter/archive/refs/tags/v5.5.tar.gz
|
||||||
|
- tar xzf v5.5.tar.gz
|
||||||
|
- cd pgFormatter-5.5
|
||||||
|
- perl Makefile.PL
|
||||||
|
- make && make install
|
||||||
|
- cd ..
|
||||||
|
- ./scripts/./sql_format_check.sh
|
||||||
|
|
||||||
# make sure api builds with default features (used by other crates relying on lemmy api)
|
# make sure api builds with default features (used by other crates relying on lemmy api)
|
||||||
check_api_common_default_features:
|
check_api_common_default_features:
|
||||||
|
@ -75,8 +102,7 @@ pipeline:
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
commands:
|
commands:
|
||||||
- cargo check --package lemmy_api_common
|
- cargo check --package lemmy_api_common
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
lemmy_api_common_doesnt_depend_on_diesel:
|
lemmy_api_common_doesnt_depend_on_diesel:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
|
@ -84,8 +110,7 @@ pipeline:
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
commands:
|
commands:
|
||||||
- "! cargo tree -p lemmy_api_common --no-default-features -i diesel"
|
- "! cargo tree -p lemmy_api_common --no-default-features -i diesel"
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
lemmy_api_common_works_with_wasm:
|
lemmy_api_common_works_with_wasm:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
|
@ -94,6 +119,7 @@ pipeline:
|
||||||
commands:
|
commands:
|
||||||
- "rustup target add wasm32-unknown-unknown"
|
- "rustup target add wasm32-unknown-unknown"
|
||||||
- "cargo check --target wasm32-unknown-unknown -p lemmy_api_common"
|
- "cargo check --target wasm32-unknown-unknown -p lemmy_api_common"
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
check_defaults_hjson_updated:
|
check_defaults_hjson_updated:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
|
@ -103,8 +129,7 @@ pipeline:
|
||||||
- export LEMMY_CONFIG_LOCATION=./config/config.hjson
|
- export LEMMY_CONFIG_LOCATION=./config/config.hjson
|
||||||
- ./scripts/update_config_defaults.sh config/defaults_current.hjson
|
- ./scripts/update_config_defaults.sh config/defaults_current.hjson
|
||||||
- diff config/defaults.hjson config/defaults_current.hjson
|
- diff config/defaults.hjson config/defaults_current.hjson
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
check_diesel_schema:
|
check_diesel_schema:
|
||||||
image: willsquire/diesel-cli
|
image: willsquire/diesel-cli
|
||||||
|
@ -115,6 +140,7 @@ pipeline:
|
||||||
- diesel migration run
|
- diesel migration run
|
||||||
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
- diesel print-schema --config-file=diesel.toml > tmp.schema
|
||||||
- diff tmp.schema crates/db_schema/src/schema.rs
|
- diff tmp.schema crates/db_schema/src/schema.rs
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
check_diesel_migration_revertable:
|
check_diesel_migration_revertable:
|
||||||
image: willsquire/diesel-cli
|
image: willsquire/diesel-cli
|
||||||
|
@ -124,13 +150,14 @@ pipeline:
|
||||||
commands:
|
commands:
|
||||||
- diesel migration run
|
- diesel migration run
|
||||||
- diesel migration redo
|
- diesel migration redo
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
cargo_clippy:
|
cargo_clippy:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
environment:
|
environment:
|
||||||
CARGO_HOME: .cargo
|
CARGO_HOME: .cargo
|
||||||
commands:
|
commands:
|
||||||
# when adding new clippy lints, make sure to also add them in scripts/fix-clippy.sh
|
# when adding new clippy lints, make sure to also add them in scripts/lint.sh
|
||||||
- rustup component add clippy
|
- rustup component add clippy
|
||||||
- cargo clippy --workspace --tests --all-targets --features console --
|
- cargo clippy --workspace --tests --all-targets --features console --
|
||||||
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
|
-D warnings -D deprecated -D clippy::perf -D clippy::complexity
|
||||||
|
@ -147,8 +174,7 @@ pipeline:
|
||||||
-D clippy::needless_collect
|
-D clippy::needless_collect
|
||||||
-D clippy::unwrap_used
|
-D clippy::unwrap_used
|
||||||
-D clippy::indexing_slicing
|
-D clippy::indexing_slicing
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
cargo_test:
|
cargo_test:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
|
@ -159,8 +185,7 @@ pipeline:
|
||||||
commands:
|
commands:
|
||||||
- export LEMMY_CONFIG_LOCATION=../../config/config.hjson
|
- export LEMMY_CONFIG_LOCATION=../../config/config.hjson
|
||||||
- cargo test --workspace --no-fail-fast
|
- cargo test --workspace --no-fail-fast
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
cargo_build:
|
cargo_build:
|
||||||
image: *muslrust_image
|
image: *muslrust_image
|
||||||
|
@ -169,8 +194,7 @@ pipeline:
|
||||||
commands:
|
commands:
|
||||||
- cargo build
|
- cargo build
|
||||||
- mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
|
- mv target/x86_64-unknown-linux-musl/debug/lemmy_server target/lemmy_server
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
run_federation_tests:
|
run_federation_tests:
|
||||||
image: node:alpine
|
image: node:alpine
|
||||||
|
@ -183,8 +207,7 @@ pipeline:
|
||||||
- cd api_tests/
|
- cd api_tests/
|
||||||
- yarn
|
- yarn
|
||||||
- yarn api-test
|
- yarn api-test
|
||||||
# when:
|
when: *slow_check_paths
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
||||||
rebuild-cache:
|
rebuild-cache:
|
||||||
image: meltwater/drone-cache:v1
|
image: meltwater/drone-cache:v1
|
||||||
|
@ -208,6 +231,7 @@ pipeline:
|
||||||
- "api_tests/node_modules"
|
- "api_tests/node_modules"
|
||||||
secrets:
|
secrets:
|
||||||
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
[MINIO_ENDPOINT, MINIO_WRITE_USER, MINIO_WRITE_PASSWORD, MINIO_BUCKET]
|
||||||
|
when: *slow_check_paths
|
||||||
|
|
||||||
publish_release_docker:
|
publish_release_docker:
|
||||||
image: woodpeckerci/plugin-docker-buildx
|
image: woodpeckerci/plugin-docker-buildx
|
||||||
|
@ -257,5 +281,3 @@ services:
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_USER: lemmy
|
POSTGRES_USER: lemmy
|
||||||
POSTGRES_PASSWORD: password
|
POSTGRES_PASSWORD: password
|
||||||
# when:
|
|
||||||
# platform: linux/amd64
|
|
||||||
|
|
2
Cargo.lock
generated
2
Cargo.lock
generated
|
@ -2728,7 +2728,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"sha2",
|
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"task-local-extensions",
|
"task-local-extensions",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -2761,7 +2760,6 @@ dependencies = [
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"sha2",
|
|
||||||
"strum",
|
"strum",
|
||||||
"strum_macros",
|
"strum_macros",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
|
|
@ -108,7 +108,6 @@ diesel_ltree = "0.3.0"
|
||||||
typed-builder = "0.15.0"
|
typed-builder = "0.15.0"
|
||||||
serial_test = "2.0.0"
|
serial_test = "2.0.0"
|
||||||
tokio = { version = "1.29.1", features = ["full"] }
|
tokio = { version = "1.29.1", features = ["full"] }
|
||||||
sha2 = "0.10.7"
|
|
||||||
regex = "1.9.0"
|
regex = "1.9.0"
|
||||||
once_cell = "1.18.0"
|
once_cell = "1.18.0"
|
||||||
diesel-derive-newtype = "2.1.0"
|
diesel-derive-newtype = "2.1.0"
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
|
"lint": "tsc --noEmit && eslint --report-unused-disable-directives --ext .js,.ts,.tsx src && prettier --check 'src/**/*.ts'",
|
||||||
"fix": "prettier --write src && eslint --fix src",
|
"fix": "prettier --write src && eslint --fix src",
|
||||||
"api-test": "jest -i follow.spec.ts && jest -i src/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"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^29.5.1",
|
"@types/jest": "^29.5.1",
|
||||||
|
|
|
@ -30,9 +30,6 @@ else
|
||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "killall existing lemmy_server processes"
|
|
||||||
killall lemmy_server || true
|
|
||||||
|
|
||||||
echo "$PWD"
|
echo "$PWD"
|
||||||
|
|
||||||
echo "start alpha"
|
echo "start alpha"
|
||||||
|
|
|
@ -7,14 +7,14 @@ pushd ..
|
||||||
cargo build
|
cargo build
|
||||||
rm target/lemmy_server || true
|
rm target/lemmy_server || true
|
||||||
cp target/debug/lemmy_server target/lemmy_server
|
cp target/debug/lemmy_server target/lemmy_server
|
||||||
|
killall -s1 lemmy_server || true
|
||||||
./api_tests/prepare-drone-federation-test.sh
|
./api_tests/prepare-drone-federation-test.sh
|
||||||
popd
|
popd
|
||||||
|
|
||||||
yarn
|
yarn
|
||||||
yarn api-test || true
|
yarn api-test || true
|
||||||
|
|
||||||
killall -s1 lemmy_server
|
killall -s1 lemmy_server || true
|
||||||
|
|
||||||
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
for INSTANCE in lemmy_alpha lemmy_beta lemmy_gamma lemmy_delta lemmy_epsilon; do
|
||||||
psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE"
|
psql "$LEMMY_DATABASE_URL" -c "DROP DATABASE $INSTANCE"
|
||||||
done
|
done
|
||||||
|
|
|
@ -30,10 +30,12 @@ import {
|
||||||
getCommentParentId,
|
getCommentParentId,
|
||||||
resolveCommunity,
|
resolveCommunity,
|
||||||
getPersonDetails,
|
getPersonDetails,
|
||||||
|
getReplies,
|
||||||
|
getUnreadCount,
|
||||||
} from "./shared";
|
} from "./shared";
|
||||||
import { CommentView } from "lemmy-js-client/dist/types/CommentView";
|
import { CommentView } from "lemmy-js-client/dist/types/CommentView";
|
||||||
|
|
||||||
let postRes: PostResponse;
|
let postOnAlphaRes: PostResponse;
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await setupLogins();
|
await setupLogins();
|
||||||
|
@ -42,7 +44,7 @@ beforeAll(async () => {
|
||||||
await followBeta(gamma);
|
await followBeta(gamma);
|
||||||
let betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
let betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
||||||
if (betaCommunity) {
|
if (betaCommunity) {
|
||||||
postRes = await createPost(alpha, betaCommunity.community.id);
|
postOnAlphaRes = await createPost(alpha, betaCommunity.community.id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -65,7 +67,7 @@ function assertCommentFederation(
|
||||||
}
|
}
|
||||||
|
|
||||||
test("Create a comment", async () => {
|
test("Create a comment", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
expect(commentRes.comment_view.comment.content).toBeDefined();
|
expect(commentRes.comment_view.comment.content).toBeDefined();
|
||||||
expect(commentRes.comment_view.community.local).toBe(false);
|
expect(commentRes.comment_view.community.local).toBe(false);
|
||||||
expect(commentRes.comment_view.creator.local).toBe(true);
|
expect(commentRes.comment_view.creator.local).toBe(true);
|
||||||
|
@ -87,7 +89,7 @@ test("Create a comment in a non-existent post", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Update a comment", async () => {
|
test("Update a comment", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
// Federate the comment first
|
// Federate the comment first
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await resolveComment(beta, commentRes.comment_view.comment)
|
await resolveComment(beta, commentRes.comment_view.comment)
|
||||||
|
@ -113,7 +115,7 @@ test("Update a comment", async () => {
|
||||||
|
|
||||||
test("Delete a comment", async () => {
|
test("Delete a comment", async () => {
|
||||||
// creating a comment on alpha (remote from home of community)
|
// creating a comment on alpha (remote from home of community)
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
|
||||||
// Find the comment on beta (home of community)
|
// Find the comment on beta (home of community)
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
|
@ -167,7 +169,7 @@ test("Delete a comment", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test.skip("Remove a comment from admin and community on the same instance", async () => {
|
test.skip("Remove a comment from admin and community on the same instance", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
|
||||||
// Get the id for beta
|
// Get the id for beta
|
||||||
let betaCommentId = (
|
let betaCommentId = (
|
||||||
|
@ -189,13 +191,14 @@ test.skip("Remove a comment from admin and community on the same instance", asyn
|
||||||
);
|
);
|
||||||
expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
|
expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
|
||||||
|
|
||||||
|
// beta will unremove the comment
|
||||||
let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
|
let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
|
||||||
expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
|
expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
|
||||||
|
|
||||||
// Make sure that comment is unremoved on beta
|
// Make sure that comment is unremoved on alpha
|
||||||
let refetchedPostComments2 = await getComments(
|
let refetchedPostComments2 = await getComments(
|
||||||
alpha,
|
alpha,
|
||||||
postRes.post_view.post.id,
|
postOnAlphaRes.post_view.post.id,
|
||||||
);
|
);
|
||||||
expect(refetchedPostComments2.comments[0].comment.removed).toBe(false);
|
expect(refetchedPostComments2.comments[0].comment.removed).toBe(false);
|
||||||
assertCommentFederation(
|
assertCommentFederation(
|
||||||
|
@ -249,7 +252,7 @@ test("Remove a comment from admin and community on different instance", async ()
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Unlike a comment", async () => {
|
test("Unlike a comment", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
|
||||||
// Lemmy automatically creates 1 like (vote) by author of comment.
|
// Lemmy automatically creates 1 like (vote) by author of comment.
|
||||||
// Make sure that comment is liked (voted up) on gamma, downstream peer
|
// Make sure that comment is liked (voted up) on gamma, downstream peer
|
||||||
|
@ -286,7 +289,7 @@ test("Unlike a comment", async () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Federated comment like", async () => {
|
test("Federated comment like", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
|
||||||
// Find the comment on beta
|
// Find the comment on beta
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
|
@ -301,13 +304,14 @@ test("Federated comment like", async () => {
|
||||||
expect(like.comment_view.counts.score).toBe(2);
|
expect(like.comment_view.counts.score).toBe(2);
|
||||||
|
|
||||||
// Get the post from alpha, check the likes
|
// Get the post from alpha, check the likes
|
||||||
let postComments = await getComments(alpha, postRes.post_view.post.id);
|
let postComments = await getComments(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
expect(postComments.comments[0].counts.score).toBe(2);
|
expect(postComments.comments[0].counts.score).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Reply to a comment", async () => {
|
test("Reply to a comment from another instance, get notification", async () => {
|
||||||
// Create a comment on alpha, find it on beta
|
// Create a root-level trunk-branch comment on alpha
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
// find that comment id on beta
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await resolveComment(beta, commentRes.comment_view.comment)
|
await resolveComment(beta, commentRes.comment_view.comment)
|
||||||
).comment;
|
).comment;
|
||||||
|
@ -316,9 +320,7 @@ test("Reply to a comment", async () => {
|
||||||
throw "Missing beta comment";
|
throw "Missing beta comment";
|
||||||
}
|
}
|
||||||
|
|
||||||
// find that comment id on beta
|
// Reply from beta, extending the branch
|
||||||
|
|
||||||
// Reply from beta
|
|
||||||
let replyRes = await createComment(
|
let replyRes = await createComment(
|
||||||
beta,
|
beta,
|
||||||
betaComment.post.id,
|
betaComment.post.id,
|
||||||
|
@ -332,11 +334,13 @@ test("Reply to a comment", async () => {
|
||||||
);
|
);
|
||||||
expect(replyRes.comment_view.counts.score).toBe(1);
|
expect(replyRes.comment_view.counts.score).toBe(1);
|
||||||
|
|
||||||
// Make sure that comment is seen on alpha
|
// Make sure that reply comment is seen on alpha
|
||||||
// TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
|
// TODO not sure why, but a searchComment back to alpha, for the ap_id of betas
|
||||||
// comment, isn't working.
|
// comment, isn't working.
|
||||||
// let searchAlpha = await searchComment(alpha, replyRes.comment);
|
// let searchAlpha = await searchComment(alpha, replyRes.comment);
|
||||||
let postComments = await getComments(alpha, postRes.post_view.post.id);
|
let postComments = await getComments(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
// Note: in Lemmy 0.18.3 pre-release this is coming up 7
|
||||||
|
expect(postComments.comments.length).toBeGreaterThanOrEqual(2);
|
||||||
let alphaComment = postComments.comments[0];
|
let alphaComment = postComments.comments[0];
|
||||||
expect(alphaComment.comment.content).toBeDefined();
|
expect(alphaComment.comment.content).toBeDefined();
|
||||||
expect(getCommentParentId(alphaComment.comment)).toBe(
|
expect(getCommentParentId(alphaComment.comment)).toBe(
|
||||||
|
@ -346,15 +350,33 @@ test("Reply to a comment", async () => {
|
||||||
expect(alphaComment.creator.local).toBe(false);
|
expect(alphaComment.creator.local).toBe(false);
|
||||||
expect(alphaComment.counts.score).toBe(1);
|
expect(alphaComment.counts.score).toBe(1);
|
||||||
assertCommentFederation(alphaComment, replyRes.comment_view);
|
assertCommentFederation(alphaComment, replyRes.comment_view);
|
||||||
|
|
||||||
|
// Did alpha get notified of the reply from beta?
|
||||||
|
let alphaUnreadCountRes = await getUnreadCount(alpha);
|
||||||
|
expect(alphaUnreadCountRes.replies).toBe(1);
|
||||||
|
|
||||||
|
// check inbox of replies on alpha, fetching read/unread both
|
||||||
|
let alphaRepliesRes = await getReplies(alpha);
|
||||||
|
expect(alphaRepliesRes.replies.length).toBe(1);
|
||||||
|
expect(alphaRepliesRes.replies[0].comment.content).toBeDefined();
|
||||||
|
expect(alphaRepliesRes.replies[0].community.local).toBe(false);
|
||||||
|
expect(alphaRepliesRes.replies[0].creator.local).toBe(false);
|
||||||
|
expect(alphaRepliesRes.replies[0].counts.score).toBe(1);
|
||||||
|
// ToDo: interesting alphaRepliesRes.replies[0].comment_reply.id is 1, meaning? how did that come about?
|
||||||
|
expect(alphaRepliesRes.replies[0].comment.id).toBe(alphaComment.comment.id);
|
||||||
|
// this is a new notification, getReplies fetch was for read/unread both, confirm it is unread.
|
||||||
|
expect(alphaRepliesRes.replies[0].comment_reply.read).toBe(false);
|
||||||
|
assertCommentFederation(alphaRepliesRes.replies[0], replyRes.comment_view);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Mention beta", async () => {
|
test("Mention beta from alpha", async () => {
|
||||||
// Create a mention on alpha
|
// Create a new branch, trunk-level comment branch, from alpha instance
|
||||||
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
|
// Create a reply comment to previous comment, this has a mention in body
|
||||||
let mentionContent = "A test mention of @lemmy_beta@lemmy-beta:8551";
|
let mentionContent = "A test mention of @lemmy_beta@lemmy-beta:8551";
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
|
||||||
let mentionRes = await createComment(
|
let mentionRes = await createComment(
|
||||||
alpha,
|
alpha,
|
||||||
postRes.post_view.post.id,
|
postOnAlphaRes.post_view.post.id,
|
||||||
commentRes.comment_view.comment.id,
|
commentRes.comment_view.comment.id,
|
||||||
mentionContent,
|
mentionContent,
|
||||||
);
|
);
|
||||||
|
@ -363,15 +385,44 @@ test("Mention beta", async () => {
|
||||||
expect(mentionRes.comment_view.creator.local).toBe(true);
|
expect(mentionRes.comment_view.creator.local).toBe(true);
|
||||||
expect(mentionRes.comment_view.counts.score).toBe(1);
|
expect(mentionRes.comment_view.counts.score).toBe(1);
|
||||||
|
|
||||||
|
// get beta's localized copy of the alpha post
|
||||||
|
let betaPost = (await resolvePost(beta, postOnAlphaRes.post_view.post)).post;
|
||||||
|
if (!betaPost) {
|
||||||
|
throw "unable to locate post on beta";
|
||||||
|
}
|
||||||
|
expect(betaPost.post.ap_id).toBe(postOnAlphaRes.post_view.post.ap_id);
|
||||||
|
expect(betaPost.post.name).toBe(postOnAlphaRes.post_view.post.name);
|
||||||
|
|
||||||
|
// Make sure that both new comments are seen on beta and have parent/child relationship
|
||||||
|
let betaPostComments = await getComments(beta, betaPost.post.id);
|
||||||
|
expect(betaPostComments.comments.length).toBeGreaterThanOrEqual(2);
|
||||||
|
// the trunk-branch root comment will be older than the mention reply comment, so index 1
|
||||||
|
let betaRootComment = betaPostComments.comments[1];
|
||||||
|
// the trunk-branch root comment should not have a parent
|
||||||
|
expect(getCommentParentId(betaRootComment.comment)).toBeUndefined();
|
||||||
|
expect(betaRootComment.comment.content).toBeDefined();
|
||||||
|
// the mention reply comment should have parent that points to the branch root level comment
|
||||||
|
expect(getCommentParentId(betaPostComments.comments[0].comment)).toBe(
|
||||||
|
betaPostComments.comments[1].comment.id,
|
||||||
|
);
|
||||||
|
expect(betaRootComment.community.local).toBe(true);
|
||||||
|
expect(betaRootComment.creator.local).toBe(false);
|
||||||
|
expect(betaRootComment.counts.score).toBe(1);
|
||||||
|
assertCommentFederation(betaRootComment, commentRes.comment_view);
|
||||||
|
|
||||||
let mentionsRes = await getMentions(beta);
|
let mentionsRes = await getMentions(beta);
|
||||||
expect(mentionsRes.mentions[0].comment.content).toBeDefined();
|
expect(mentionsRes.mentions[0].comment.content).toBeDefined();
|
||||||
expect(mentionsRes.mentions[0].community.local).toBe(true);
|
expect(mentionsRes.mentions[0].community.local).toBe(true);
|
||||||
expect(mentionsRes.mentions[0].creator.local).toBe(false);
|
expect(mentionsRes.mentions[0].creator.local).toBe(false);
|
||||||
expect(mentionsRes.mentions[0].counts.score).toBe(1);
|
expect(mentionsRes.mentions[0].counts.score).toBe(1);
|
||||||
|
// the reply comment with mention should be the most fresh, newest, index 0
|
||||||
|
expect(mentionsRes.mentions[0].person_mention.comment_id).toBe(
|
||||||
|
betaPostComments.comments[0].comment.id,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Comment Search", async () => {
|
test("Comment Search", async () => {
|
||||||
let commentRes = await createComment(alpha, postRes.post_view.post.id);
|
let commentRes = await createComment(alpha, postOnAlphaRes.post_view.post.id);
|
||||||
let betaComment = (
|
let betaComment = (
|
||||||
await resolveComment(beta, commentRes.comment_view.comment)
|
await resolveComment(beta, commentRes.comment_view.comment)
|
||||||
).comment;
|
).comment;
|
||||||
|
@ -496,13 +547,13 @@ test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
||||||
).toBe(0);
|
).toBe(0);
|
||||||
|
|
||||||
// B creates a post, and two comments, should be invisible to A
|
// B creates a post, and two comments, should be invisible to A
|
||||||
let postRes = await createPost(beta, 2);
|
let postOnBetaRes = await createPost(beta, 2);
|
||||||
expect(postRes.post_view.post.name).toBeDefined();
|
expect(postOnBetaRes.post_view.post.name).toBeDefined();
|
||||||
|
|
||||||
let parentCommentContent = "An invisible top level comment from beta";
|
let parentCommentContent = "An invisible top level comment from beta";
|
||||||
let parentCommentRes = await createComment(
|
let parentCommentRes = await createComment(
|
||||||
beta,
|
beta,
|
||||||
postRes.post_view.post.id,
|
postOnBetaRes.post_view.post.id,
|
||||||
undefined,
|
undefined,
|
||||||
parentCommentContent,
|
parentCommentContent,
|
||||||
);
|
);
|
||||||
|
@ -514,7 +565,7 @@ test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
||||||
let childCommentContent = "An invisible child comment from beta";
|
let childCommentContent = "An invisible child comment from beta";
|
||||||
let childCommentRes = await createComment(
|
let childCommentRes = await createComment(
|
||||||
beta,
|
beta,
|
||||||
postRes.post_view.post.id,
|
postOnBetaRes.post_view.post.id,
|
||||||
parentCommentRes.comment_view.comment.id,
|
parentCommentRes.comment_view.comment.id,
|
||||||
childCommentContent,
|
childCommentContent,
|
||||||
);
|
);
|
||||||
|
@ -537,7 +588,8 @@ test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
||||||
expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
|
expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
|
||||||
|
|
||||||
// Get the post from alpha
|
// Get the post from alpha
|
||||||
let alphaPostB = (await resolvePost(alpha, postRes.post_view.post)).post;
|
let alphaPostB = (await resolvePost(alpha, postOnBetaRes.post_view.post))
|
||||||
|
.post;
|
||||||
if (!alphaPostB) {
|
if (!alphaPostB) {
|
||||||
throw "Missing alpha post B";
|
throw "Missing alpha post B";
|
||||||
}
|
}
|
||||||
|
@ -564,10 +616,11 @@ test("Report a comment", async () => {
|
||||||
if (!betaCommunity) {
|
if (!betaCommunity) {
|
||||||
throw "Missing beta community";
|
throw "Missing beta community";
|
||||||
}
|
}
|
||||||
let postRes = (await createPost(beta, betaCommunity.community.id)).post_view
|
let postOnBetaRes = (await createPost(beta, betaCommunity.community.id))
|
||||||
.post;
|
.post_view.post;
|
||||||
expect(postRes).toBeDefined();
|
expect(postOnBetaRes).toBeDefined();
|
||||||
let commentRes = (await createComment(beta, postRes.id)).comment_view.comment;
|
let commentRes = (await createComment(beta, postOnBetaRes.id)).comment_view
|
||||||
|
.comment;
|
||||||
expect(commentRes).toBeDefined();
|
expect(commentRes).toBeDefined();
|
||||||
|
|
||||||
let alphaComment = (await resolveComment(alpha, commentRes)).comment?.comment;
|
let alphaComment = (await resolveComment(alpha, commentRes)).comment?.comment;
|
||||||
|
|
|
@ -1,4 +1,11 @@
|
||||||
import { LemmyHttp } from "lemmy-js-client";
|
import {
|
||||||
|
GetReplies,
|
||||||
|
GetRepliesResponse,
|
||||||
|
GetUnreadCount,
|
||||||
|
GetUnreadCountResponse,
|
||||||
|
LemmyHttp,
|
||||||
|
LocalUser,
|
||||||
|
} from "lemmy-js-client";
|
||||||
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
import { CreatePost } from "lemmy-js-client/dist/types/CreatePost";
|
||||||
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
|
import { DeletePost } from "lemmy-js-client/dist/types/DeletePost";
|
||||||
import { EditPost } from "lemmy-js-client/dist/types/EditPost";
|
import { EditPost } from "lemmy-js-client/dist/types/EditPost";
|
||||||
|
@ -325,6 +332,24 @@ export async function getComments(
|
||||||
return api.client.getComments(form);
|
return api.client.getComments(form);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUnreadCount(
|
||||||
|
api: API,
|
||||||
|
): Promise<GetUnreadCountResponse> {
|
||||||
|
let form: GetUnreadCount = {
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
return api.client.getUnreadCount(form);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getReplies(api: API): Promise<GetRepliesResponse> {
|
||||||
|
let form: GetReplies = {
|
||||||
|
sort: "New",
|
||||||
|
unread_only: false,
|
||||||
|
auth: api.auth,
|
||||||
|
};
|
||||||
|
return api.client.getReplies(form);
|
||||||
|
}
|
||||||
|
|
||||||
export async function resolveComment(
|
export async function resolveComment(
|
||||||
api: API,
|
api: API,
|
||||||
comment: Comment,
|
comment: Comment,
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_comment_response,
|
build_response::build_comment_response,
|
||||||
comment::{CommentResponse, CreateCommentLike},
|
comment::{CommentResponse, CreateCommentLike},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
|
utils::{check_community_ban, check_downvotes_enabled, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -17,70 +18,80 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
use lemmy_db_views::structs::{CommentView, LocalUserView};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for CreateCommentLike {
|
pub async fn like_comment(
|
||||||
type Response = CommentResponse;
|
data: Json<CreateCommentLike>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommentResponse>, LemmyError> {
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let mut recipient_ids = Vec::<LocalUserId>::new();
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
|
|
||||||
let data: &CreateCommentLike = self;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let mut recipient_ids = Vec::<LocalUserId>::new();
|
// Don't do a downvote if site has downvotes disabled
|
||||||
|
check_downvotes_enabled(data.score, &local_site)?;
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
let comment_id = data.comment_id;
|
||||||
check_downvotes_enabled(data.score, &local_site)?;
|
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
check_community_ban(
|
||||||
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
local_user_view.person.id,
|
||||||
|
orig_comment.community.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Add parent poster or commenter to recipients
|
||||||
local_user_view.person.id,
|
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
|
||||||
orig_comment.community.id,
|
if let Ok(reply) = comment_reply {
|
||||||
&mut context.pool(),
|
let recipient_id = reply.recipient_id;
|
||||||
)
|
if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
|
||||||
.await?;
|
{
|
||||||
|
recipient_ids.push(local_recipient.local_user.id);
|
||||||
// Add parent poster or commenter to recipients
|
|
||||||
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
|
|
||||||
if let Ok(reply) = comment_reply {
|
|
||||||
let recipient_id = reply.recipient_id;
|
|
||||||
if let Ok(local_recipient) =
|
|
||||||
LocalUserView::read_person(&mut context.pool(), recipient_id).await
|
|
||||||
{
|
|
||||||
recipient_ids.push(local_recipient.local_user.id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let like_form = CommentLikeForm {
|
let like_form = CommentLikeForm {
|
||||||
comment_id: data.comment_id,
|
comment_id: data.comment_id,
|
||||||
post_id: orig_comment.post.id,
|
post_id: orig_comment.post.id,
|
||||||
person_id: local_user_view.person.id,
|
person_id: local_user_view.person.id,
|
||||||
score: data.score,
|
score: data.score,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove any likes first
|
// Remove any likes first
|
||||||
let person_id = local_user_view.person.id;
|
let person_id = local_user_view.person.id;
|
||||||
|
|
||||||
CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
|
CommentLike::remove(&mut context.pool(), person_id, comment_id).await?;
|
||||||
|
|
||||||
// Only add the like if the score isnt 0
|
// Only add the like if the score isnt 0
|
||||||
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
||||||
if do_add {
|
if do_add {
|
||||||
CommentLike::like(&mut context.pool(), &like_form)
|
CommentLike::like(&mut context.pool(), &like_form)
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
|
.with_lemmy_type(LemmyErrorType::CouldntLikeComment)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::LikePostOrComment(
|
||||||
|
orig_comment.comment.ap_id,
|
||||||
|
local_user_view.person.clone(),
|
||||||
|
orig_comment.community,
|
||||||
|
data.score,
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
build_comment_response(
|
build_comment_response(
|
||||||
context,
|
context.deref(),
|
||||||
comment_id,
|
comment_id,
|
||||||
Some(local_user_view),
|
Some(local_user_view),
|
||||||
None,
|
None,
|
||||||
recipient_ids,
|
recipient_ids,
|
||||||
)
|
)
|
||||||
.await
|
.await?,
|
||||||
}
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::{check_report_reason, Perform};
|
use crate::check_report_reason;
|
||||||
use actix_web::web::Data;
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
comment::{CommentReportResponse, CreateCommentReport},
|
comment::{CommentReportResponse, CreateCommentReport},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
local_user_view_from_jwt,
|
local_user_view_from_jwt,
|
||||||
|
@ -21,55 +23,60 @@ use lemmy_db_views::structs::{CommentReportView, CommentView};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
/// Creates a comment report and notifies the moderators of the community
|
/// Creates a comment report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for CreateCommentReport {
|
pub async fn create_comment_report(
|
||||||
type Response = CommentReportResponse;
|
data: Json<CreateCommentReport>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommentReportResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let reason = sanitize_html(data.reason.trim());
|
||||||
async fn perform(
|
check_report_reason(&reason, &local_site)?;
|
||||||
&self,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<CommentReportResponse, LemmyError> {
|
|
||||||
let data: &CreateCommentReport = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let reason = sanitize_html(self.reason.trim());
|
let person_id = local_user_view.person.id;
|
||||||
check_report_reason(&reason, &local_site)?;
|
let comment_id = data.comment_id;
|
||||||
|
let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
check_community_ban(person_id, comment_view.community.id, &mut context.pool()).await?;
|
||||||
let comment_id = data.comment_id;
|
|
||||||
let comment_view = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
|
||||||
|
|
||||||
check_community_ban(person_id, comment_view.community.id, &mut context.pool()).await?;
|
let report_form = CommentReportForm {
|
||||||
|
creator_id: person_id,
|
||||||
|
comment_id,
|
||||||
|
original_comment_text: comment_view.comment.content,
|
||||||
|
reason,
|
||||||
|
};
|
||||||
|
|
||||||
let report_form = CommentReportForm {
|
let report = CommentReport::report(&mut context.pool(), &report_form)
|
||||||
creator_id: person_id,
|
.await
|
||||||
comment_id,
|
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
|
||||||
original_comment_text: comment_view.comment.content,
|
|
||||||
reason,
|
|
||||||
};
|
|
||||||
|
|
||||||
let report = CommentReport::report(&mut context.pool(), &report_form)
|
let comment_report_view =
|
||||||
.await
|
CommentReportView::read(&mut context.pool(), report.id, person_id).await?;
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
|
|
||||||
|
|
||||||
let comment_report_view =
|
// Email the admins
|
||||||
CommentReportView::read(&mut context.pool(), report.id, person_id).await?;
|
if local_site.reports_email_admins {
|
||||||
|
send_new_report_email_to_admins(
|
||||||
// Email the admins
|
&comment_report_view.creator.name,
|
||||||
if local_site.reports_email_admins {
|
&comment_report_view.comment_creator.name,
|
||||||
send_new_report_email_to_admins(
|
&mut context.pool(),
|
||||||
&comment_report_view.creator.name,
|
context.settings(),
|
||||||
&comment_report_view.comment_creator.name,
|
)
|
||||||
&mut context.pool(),
|
.await?;
|
||||||
context.settings(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(CommentReportResponse {
|
|
||||||
comment_report_view,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::CreateReport(
|
||||||
|
comment_view.comment.ap_id.inner().clone(),
|
||||||
|
local_user_view.person,
|
||||||
|
comment_view.community,
|
||||||
|
data.reason.clone(),
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(CommentReportResponse {
|
||||||
|
comment_report_view,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
community::{AddModToCommunity, AddModToCommunityResponse},
|
community::{AddModToCommunity, AddModToCommunityResponse},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{is_mod_or_admin, local_user_view_from_jwt},
|
utils::{is_mod_or_admin, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,58 +16,62 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for AddModToCommunity {
|
pub async fn add_mod_to_community(
|
||||||
type Response = AddModToCommunityResponse;
|
data: Json<AddModToCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<AddModToCommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let community_id = data.community_id;
|
||||||
async fn perform(
|
|
||||||
&self,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<AddModToCommunityResponse, LemmyError> {
|
|
||||||
let data: &AddModToCommunity = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
// Verify that only mods or admins can add mod
|
||||||
|
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?;
|
||||||
// Verify that only mods or admins can add mod
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||||
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?;
|
if local_user_view.person.admin && !community.local {
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
return Err(LemmyErrorType::NotAModerator)?;
|
||||||
if local_user_view.person.admin && !community.local {
|
|
||||||
return Err(LemmyErrorType::NotAModerator)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update in local database
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
|
||||||
community_id: data.community_id,
|
|
||||||
person_id: data.person_id,
|
|
||||||
};
|
|
||||||
if data.added {
|
|
||||||
CommunityModerator::join(&mut context.pool(), &community_moderator_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
|
||||||
} else {
|
|
||||||
CommunityModerator::leave(&mut context.pool(), &community_moderator_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mod tables
|
|
||||||
let form = ModAddCommunityForm {
|
|
||||||
mod_person_id: local_user_view.person.id,
|
|
||||||
other_person_id: data.person_id,
|
|
||||||
community_id: data.community_id,
|
|
||||||
removed: Some(!data.added),
|
|
||||||
};
|
|
||||||
|
|
||||||
ModAddCommunity::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
|
|
||||||
// updated once we receive an activity from the community (like `Announce/Add/Moderator`)
|
|
||||||
let community_id = data.community_id;
|
|
||||||
let moderators =
|
|
||||||
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
|
||||||
|
|
||||||
Ok(AddModToCommunityResponse { moderators })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update in local database
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id: data.community_id,
|
||||||
|
person_id: data.person_id,
|
||||||
|
};
|
||||||
|
if data.added {
|
||||||
|
CommunityModerator::join(&mut context.pool(), &community_moderator_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
||||||
|
} else {
|
||||||
|
CommunityModerator::leave(&mut context.pool(), &community_moderator_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mod tables
|
||||||
|
let form = ModAddCommunityForm {
|
||||||
|
mod_person_id: local_user_view.person.id,
|
||||||
|
other_person_id: data.person_id,
|
||||||
|
community_id: data.community_id,
|
||||||
|
removed: Some(!data.added),
|
||||||
|
};
|
||||||
|
|
||||||
|
ModAddCommunity::create(&mut context.pool(), &form).await?;
|
||||||
|
|
||||||
|
// Note: in case a remote mod is added, this returns the old moderators list, it will only get
|
||||||
|
// updated once we receive an activity from the community (like `Announce/Add/Moderator`)
|
||||||
|
let community_id = data.community_id;
|
||||||
|
let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::AddModToCommunity(
|
||||||
|
local_user_view.person,
|
||||||
|
data.community_id,
|
||||||
|
data.person_id,
|
||||||
|
data.added,
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(AddModToCommunityResponse { moderators }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
community::{BanFromCommunity, BanFromCommunityResponse},
|
community::{BanFromCommunity, BanFromCommunityResponse},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
is_mod_or_admin,
|
is_mod_or_admin,
|
||||||
local_user_view_from_jwt,
|
local_user_view_from_jwt,
|
||||||
|
@ -28,77 +29,85 @@ use lemmy_utils::{
|
||||||
utils::{time::naive_from_unix, validation::is_valid_body_field},
|
utils::{time::naive_from_unix, validation::is_valid_body_field},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for BanFromCommunity {
|
pub async fn ban_from_community(
|
||||||
type Response = BanFromCommunityResponse;
|
data: Json<BanFromCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<BanFromCommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let banned_person_id = data.person_id;
|
||||||
async fn perform(
|
let remove_data = data.remove_data.unwrap_or(false);
|
||||||
&self,
|
let expires = data.expires.map(naive_from_unix);
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<BanFromCommunityResponse, LemmyError> {
|
|
||||||
let data: &BanFromCommunity = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
// Verify that only mods or admins can ban
|
||||||
let banned_person_id = data.person_id;
|
is_mod_or_admin(
|
||||||
let remove_data = data.remove_data.unwrap_or(false);
|
&mut context.pool(),
|
||||||
let expires = data.expires.map(naive_from_unix);
|
local_user_view.person.id,
|
||||||
|
data.community_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
is_valid_body_field(&data.reason, false)?;
|
||||||
|
|
||||||
// Verify that only mods or admins can ban
|
let community_user_ban_form = CommunityPersonBanForm {
|
||||||
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?;
|
community_id: data.community_id,
|
||||||
is_valid_body_field(&data.reason, false)?;
|
person_id: data.person_id,
|
||||||
|
expires: Some(expires),
|
||||||
|
};
|
||||||
|
|
||||||
let community_user_ban_form = CommunityPersonBanForm {
|
if data.ban {
|
||||||
|
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
|
||||||
|
|
||||||
|
// Also unsubscribe them from the community, if they are subscribed
|
||||||
|
let community_follower_form = CommunityFollowerForm {
|
||||||
community_id: data.community_id,
|
community_id: data.community_id,
|
||||||
person_id: data.person_id,
|
person_id: banned_person_id,
|
||||||
expires: Some(expires),
|
pending: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.ban {
|
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
||||||
CommunityPersonBan::ban(&mut context.pool(), &community_user_ban_form)
|
.await
|
||||||
.await
|
.ok();
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
|
} else {
|
||||||
|
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form)
|
||||||
// Also unsubscribe them from the community, if they are subscribed
|
.await
|
||||||
let community_follower_form = CommunityFollowerForm {
|
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
|
||||||
community_id: data.community_id,
|
|
||||||
person_id: banned_person_id,
|
|
||||||
pending: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
} else {
|
|
||||||
CommunityPersonBan::unban(&mut context.pool(), &community_user_ban_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityUserAlreadyBanned)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove/Restore their data if that's desired
|
|
||||||
if remove_data {
|
|
||||||
remove_user_data_in_community(community_id, banned_person_id, &mut context.pool()).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mod tables
|
|
||||||
let form = ModBanFromCommunityForm {
|
|
||||||
mod_person_id: local_user_view.person.id,
|
|
||||||
other_person_id: data.person_id,
|
|
||||||
community_id: data.community_id,
|
|
||||||
reason: sanitize_html_opt(&data.reason),
|
|
||||||
banned: Some(data.ban),
|
|
||||||
expires,
|
|
||||||
};
|
|
||||||
|
|
||||||
ModBanFromCommunity::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
let person_id = data.person_id;
|
|
||||||
let person_view = PersonView::read(&mut context.pool(), person_id).await?;
|
|
||||||
|
|
||||||
Ok(BanFromCommunityResponse {
|
|
||||||
person_view,
|
|
||||||
banned: data.ban,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove/Restore their data if that's desired
|
||||||
|
if remove_data {
|
||||||
|
remove_user_data_in_community(data.community_id, banned_person_id, &mut context.pool()).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mod tables
|
||||||
|
let form = ModBanFromCommunityForm {
|
||||||
|
mod_person_id: local_user_view.person.id,
|
||||||
|
other_person_id: data.person_id,
|
||||||
|
community_id: data.community_id,
|
||||||
|
reason: sanitize_html_opt(&data.reason),
|
||||||
|
banned: Some(data.ban),
|
||||||
|
expires,
|
||||||
|
};
|
||||||
|
|
||||||
|
ModBanFromCommunity::create(&mut context.pool(), &form).await?;
|
||||||
|
|
||||||
|
let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::BanFromCommunity(
|
||||||
|
local_user_view.person,
|
||||||
|
data.community_id,
|
||||||
|
person_view.person.clone(),
|
||||||
|
data.0.clone(),
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(BanFromCommunityResponse {
|
||||||
|
person_view,
|
||||||
|
banned: data.ban,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
community::{BlockCommunity, BlockCommunityResponse},
|
community::{BlockCommunity, BlockCommunityResponse},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::local_user_view_from_jwt,
|
utils::local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,52 +16,56 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views_actor::structs::CommunityView;
|
use lemmy_db_views_actor::structs::CommunityView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for BlockCommunity {
|
pub async fn block_community(
|
||||||
type Response = BlockCommunityResponse;
|
data: Json<BlockCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<BlockCommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let community_id = data.community_id;
|
||||||
async fn perform(
|
let person_id = local_user_view.person.id;
|
||||||
&self,
|
let community_block_form = CommunityBlockForm {
|
||||||
context: &Data<LemmyContext>,
|
person_id,
|
||||||
) -> Result<BlockCommunityResponse, LemmyError> {
|
community_id,
|
||||||
let data: &BlockCommunity = self;
|
};
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
if data.block {
|
||||||
let person_id = local_user_view.person.id;
|
CommunityBlock::block(&mut context.pool(), &community_block_form)
|
||||||
let community_block_form = CommunityBlockForm {
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
|
||||||
|
|
||||||
|
// Also, unfollow the community, and send a federated unfollow
|
||||||
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
community_id: data.community_id,
|
||||||
person_id,
|
person_id,
|
||||||
community_id,
|
pending: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
if data.block {
|
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
||||||
CommunityBlock::block(&mut context.pool(), &community_block_form)
|
.await
|
||||||
.await
|
.ok();
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
|
} else {
|
||||||
|
CommunityBlock::unblock(&mut context.pool(), &community_block_form)
|
||||||
// Also, unfollow the community, and send a federated unfollow
|
.await
|
||||||
let community_follower_form = CommunityFollowerForm {
|
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
|
||||||
community_id: data.community_id,
|
|
||||||
person_id,
|
|
||||||
pending: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
|
||||||
.await
|
|
||||||
.ok();
|
|
||||||
} else {
|
|
||||||
CommunityBlock::unblock(&mut context.pool(), &community_block_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityBlockAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let community_view =
|
|
||||||
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
|
|
||||||
|
|
||||||
Ok(BlockCommunityResponse {
|
|
||||||
blocked: data.block,
|
|
||||||
community_view,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let community_view =
|
||||||
|
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::FollowCommunity(
|
||||||
|
community_view.community.clone(),
|
||||||
|
local_user_view.person.clone(),
|
||||||
|
false,
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(BlockCommunityResponse {
|
||||||
|
blocked: data.block,
|
||||||
|
community_view,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
community::{CommunityResponse, FollowCommunity},
|
community::{CommunityResponse, FollowCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
|
utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,54 +16,56 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views_actor::structs::CommunityView;
|
use lemmy_db_views_actor::structs::CommunityView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for FollowCommunity {
|
pub async fn follow_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<FollowCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let community = Community::read(&mut context.pool(), data.community_id).await?;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
let mut community_follower_form = CommunityFollowerForm {
|
||||||
let data: &FollowCommunity = self;
|
community_id: community.id,
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
person_id: local_user_view.person.id,
|
||||||
|
pending: false,
|
||||||
|
};
|
||||||
|
|
||||||
let community_id = data.community_id;
|
if data.follow {
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
if community.local {
|
||||||
let mut community_follower_form = CommunityFollowerForm {
|
check_community_ban(local_user_view.person.id, community.id, &mut context.pool()).await?;
|
||||||
community_id: data.community_id,
|
check_community_deleted_or_removed(community.id, &mut context.pool()).await?;
|
||||||
person_id: local_user_view.person.id,
|
|
||||||
pending: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
if data.follow {
|
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
||||||
if community.local {
|
.await
|
||||||
check_community_ban(local_user_view.person.id, community_id, &mut context.pool()).await?;
|
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
||||||
check_community_deleted_or_removed(community_id, &mut context.pool()).await?;
|
} else {
|
||||||
|
// Mark as pending, the actual federation activity is sent via `SendActivity` handler
|
||||||
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
community_follower_form.pending = true;
|
||||||
.await
|
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
|
||||||
} else {
|
|
||||||
// Mark as pending, the actual federation activity is sent via `SendActivity` handler
|
|
||||||
community_follower_form.pending = true;
|
|
||||||
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !data.follow {
|
|
||||||
CommunityFollower::unfollow(&mut context.pool(), &community_follower_form)
|
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let community_id = data.community_id;
|
|
||||||
let person_id = local_user_view.person.id;
|
|
||||||
let community_view =
|
|
||||||
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
|
|
||||||
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
|
|
||||||
|
|
||||||
Ok(Self::Response {
|
|
||||||
community_view,
|
|
||||||
discussion_languages,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
if !data.follow {
|
||||||
|
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?;
|
||||||
|
|
||||||
|
let community_id = data.community_id;
|
||||||
|
let person_id = local_user_view.person.id;
|
||||||
|
let community_view =
|
||||||
|
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
|
||||||
|
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
|
||||||
|
|
||||||
|
Ok(Json(CommunityResponse {
|
||||||
|
community_view,
|
||||||
|
discussion_languages,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, HideCommunity},
|
community::{CommunityResponse, HideCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt},
|
utils::{is_admin, local_user_view_from_jwt, sanitize_html_opt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,36 +16,38 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for HideCommunity {
|
pub async fn hide_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<HideCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
// Verify its a admin (only admin can hide or unhide it)
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let community_form = CommunityUpdateForm::builder()
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
.hidden(Some(data.hidden))
|
||||||
let data: &HideCommunity = self;
|
.build();
|
||||||
|
|
||||||
// Verify its a admin (only admin can hide or unhide it)
|
let mod_hide_community_form = ModHideCommunityForm {
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
community_id: data.community_id,
|
||||||
is_admin(&local_user_view)?;
|
mod_person_id: local_user_view.person.id,
|
||||||
|
reason: sanitize_html_opt(&data.reason),
|
||||||
|
hidden: Some(data.hidden),
|
||||||
|
};
|
||||||
|
|
||||||
let community_form = CommunityUpdateForm::builder()
|
let community_id = data.community_id;
|
||||||
.hidden(Some(data.hidden))
|
let community = Community::update(&mut context.pool(), community_id, &community_form)
|
||||||
.build();
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?;
|
||||||
|
|
||||||
let mod_hide_community_form = ModHideCommunityForm {
|
ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?;
|
||||||
community_id: data.community_id,
|
|
||||||
mod_person_id: local_user_view.person.id,
|
|
||||||
reason: sanitize_html_opt(&data.reason),
|
|
||||||
hidden: Some(data.hidden),
|
|
||||||
};
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
ActivityChannel::submit_activity(
|
||||||
Community::update(&mut context.pool(), community_id, &community_form)
|
SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
|
||||||
.await
|
&context,
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?;
|
build_community_response(&context, local_user_view, community_id).await
|
||||||
|
|
||||||
build_community_response(context, local_user_view, community_id).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod add_mod;
|
pub mod add_mod;
|
||||||
mod ban;
|
pub mod ban;
|
||||||
mod block;
|
pub mod block;
|
||||||
mod follow;
|
pub mod follow;
|
||||||
mod hide;
|
pub mod hide;
|
||||||
mod transfer;
|
pub mod transfer;
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::{BanPerson, BanPersonResponse},
|
person::{BanPerson, BanPersonResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{is_admin, local_user_view_from_jwt, remove_user_data, sanitize_html_opt},
|
utils::{is_admin, local_user_view_from_jwt, remove_user_data, sanitize_html_opt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -17,65 +18,68 @@ use lemmy_utils::{
|
||||||
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
|
error::{LemmyError, LemmyErrorExt, LemmyErrorType},
|
||||||
utils::{time::naive_from_unix, validation::is_valid_body_field},
|
utils::{time::naive_from_unix, validation::is_valid_body_field},
|
||||||
};
|
};
|
||||||
|
#[tracing::instrument(skip(context))]
|
||||||
|
pub async fn ban_from_site(
|
||||||
|
data: Json<BanPerson>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<BanPersonResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
// Make sure user is an admin
|
||||||
impl Perform for BanPerson {
|
is_admin(&local_user_view)?;
|
||||||
type Response = BanPersonResponse;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
is_valid_body_field(&data.reason, false)?;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<BanPersonResponse, LemmyError> {
|
|
||||||
let data: &BanPerson = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
// Make sure user is an admin
|
let expires = data.expires.map(naive_from_unix);
|
||||||
is_admin(&local_user_view)?;
|
|
||||||
|
|
||||||
is_valid_body_field(&data.reason, false)?;
|
let person = Person::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
data.person_id,
|
||||||
|
&PersonUpdateForm::builder()
|
||||||
|
.banned(Some(data.ban))
|
||||||
|
.ban_expires(Some(expires))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
|
||||||
|
|
||||||
let ban = data.ban;
|
// Remove their data if that's desired
|
||||||
let banned_person_id = data.person_id;
|
let remove_data = data.remove_data.unwrap_or(false);
|
||||||
let expires = data.expires.map(naive_from_unix);
|
if remove_data {
|
||||||
|
remove_user_data(
|
||||||
let person = Person::update(
|
person.id,
|
||||||
&mut context.pool(),
|
&mut context.pool(),
|
||||||
banned_person_id,
|
context.settings(),
|
||||||
&PersonUpdateForm::builder()
|
context.client(),
|
||||||
.banned(Some(ban))
|
|
||||||
.ban_expires(Some(expires))
|
|
||||||
.build(),
|
|
||||||
)
|
)
|
||||||
.await
|
.await?;
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateUser)?;
|
|
||||||
|
|
||||||
// Remove their data if that's desired
|
|
||||||
let remove_data = data.remove_data.unwrap_or(false);
|
|
||||||
if remove_data {
|
|
||||||
remove_user_data(
|
|
||||||
person.id,
|
|
||||||
&mut context.pool(),
|
|
||||||
context.settings(),
|
|
||||||
context.client(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mod tables
|
|
||||||
let form = ModBanForm {
|
|
||||||
mod_person_id: local_user_view.person.id,
|
|
||||||
other_person_id: data.person_id,
|
|
||||||
reason: sanitize_html_opt(&data.reason),
|
|
||||||
banned: Some(data.ban),
|
|
||||||
expires,
|
|
||||||
};
|
|
||||||
|
|
||||||
ModBan::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
let person_id = data.person_id;
|
|
||||||
let person_view = PersonView::read(&mut context.pool(), person_id).await?;
|
|
||||||
|
|
||||||
Ok(BanPersonResponse {
|
|
||||||
person_view,
|
|
||||||
banned: data.ban,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mod tables
|
||||||
|
let form = ModBanForm {
|
||||||
|
mod_person_id: local_user_view.person.id,
|
||||||
|
other_person_id: data.person_id,
|
||||||
|
reason: sanitize_html_opt(&data.reason),
|
||||||
|
banned: Some(data.ban),
|
||||||
|
expires,
|
||||||
|
};
|
||||||
|
|
||||||
|
ModBan::create(&mut context.pool(), &form).await?;
|
||||||
|
|
||||||
|
let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::BanFromSite(
|
||||||
|
local_user_view.person,
|
||||||
|
person_view.person.clone(),
|
||||||
|
data.0.clone(),
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(BanPersonResponse {
|
||||||
|
person_view,
|
||||||
|
banned: data.ban,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{FeaturePost, PostResponse},
|
post::{FeaturePost, PostResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -22,67 +23,65 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for FeaturePost {
|
pub async fn feature_post(
|
||||||
type Response = PostResponse;
|
data: Json<FeaturePost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let post_id = data.post_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
let data: &FeaturePost = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
check_community_ban(
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
local_user_view.person.id,
|
||||||
|
orig_post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
check_community_ban(
|
if data.feature_type == PostFeatureType::Community {
|
||||||
|
// Verify that only the mods can feature in community
|
||||||
|
is_mod_or_admin(
|
||||||
|
&mut context.pool(),
|
||||||
local_user_view.person.id,
|
local_user_view.person.id,
|
||||||
orig_post.community_id,
|
orig_post.community_id,
|
||||||
&mut context.pool(),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
} else {
|
||||||
|
is_admin(&local_user_view)?;
|
||||||
if data.feature_type == PostFeatureType::Community {
|
|
||||||
// Verify that only the mods can feature in community
|
|
||||||
is_mod_or_admin(
|
|
||||||
&mut context.pool(),
|
|
||||||
local_user_view.person.id,
|
|
||||||
orig_post.community_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
} else {
|
|
||||||
is_admin(&local_user_view)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the post
|
|
||||||
let post_id = data.post_id;
|
|
||||||
let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community {
|
|
||||||
PostUpdateForm::builder()
|
|
||||||
.featured_community(Some(data.featured))
|
|
||||||
.build()
|
|
||||||
} else {
|
|
||||||
PostUpdateForm::builder()
|
|
||||||
.featured_local(Some(data.featured))
|
|
||||||
.build()
|
|
||||||
};
|
|
||||||
Post::update(&mut context.pool(), post_id, &new_post).await?;
|
|
||||||
|
|
||||||
// Mod tables
|
|
||||||
let form = ModFeaturePostForm {
|
|
||||||
mod_person_id: local_user_view.person.id,
|
|
||||||
post_id: data.post_id,
|
|
||||||
featured: data.featured,
|
|
||||||
is_featured_community: data.feature_type == PostFeatureType::Community,
|
|
||||||
};
|
|
||||||
|
|
||||||
ModFeaturePost::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
build_post_response(
|
|
||||||
context,
|
|
||||||
orig_post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the post
|
||||||
|
let post_id = data.post_id;
|
||||||
|
let new_post: PostUpdateForm = if data.feature_type == PostFeatureType::Community {
|
||||||
|
PostUpdateForm::builder()
|
||||||
|
.featured_community(Some(data.featured))
|
||||||
|
.build()
|
||||||
|
} else {
|
||||||
|
PostUpdateForm::builder()
|
||||||
|
.featured_local(Some(data.featured))
|
||||||
|
.build()
|
||||||
|
};
|
||||||
|
let post = Post::update(&mut context.pool(), post_id, &new_post).await?;
|
||||||
|
|
||||||
|
// Mod tables
|
||||||
|
let form = ModFeaturePostForm {
|
||||||
|
mod_person_id: local_user_view.person.id,
|
||||||
|
post_id: data.post_id,
|
||||||
|
featured: data.featured,
|
||||||
|
is_featured_community: data.feature_type == PostFeatureType::Community,
|
||||||
|
};
|
||||||
|
|
||||||
|
ModFeaturePost::create(&mut context.pool(), &form).await?;
|
||||||
|
|
||||||
|
let person_id = local_user_view.person.id;
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::FeaturePost(post, local_user_view.person, data.featured),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
build_post_response(&context, orig_post.community_id, person_id, post_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePostLike, PostResponse},
|
post::{CreatePostLike, PostResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -14,66 +15,76 @@ use lemmy_api_common::{
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
|
community::Community,
|
||||||
local_site::LocalSite,
|
local_site::LocalSite,
|
||||||
post::{Post, PostLike, PostLikeForm},
|
post::{Post, PostLike, PostLikeForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable},
|
traits::{Crud, Likeable},
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for CreatePostLike {
|
pub async fn like_post(
|
||||||
type Response = PostResponse;
|
data: Json<CreatePostLike>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
// Don't do a downvote if site has downvotes disabled
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
check_downvotes_enabled(data.score, &local_site)?;
|
||||||
let data: &CreatePostLike = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
// Don't do a downvote if site has downvotes disabled
|
// Check for a community ban
|
||||||
check_downvotes_enabled(data.score, &local_site)?;
|
let post_id = data.post_id;
|
||||||
|
let post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
|
|
||||||
// Check for a community ban
|
check_community_ban(
|
||||||
let post_id = data.post_id;
|
local_user_view.person.id,
|
||||||
let post = Post::read(&mut context.pool(), post_id).await?;
|
post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
check_community_ban(
|
let like_form = PostLikeForm {
|
||||||
local_user_view.person.id,
|
post_id: data.post_id,
|
||||||
post.community_id,
|
person_id: local_user_view.person.id,
|
||||||
&mut context.pool(),
|
score: data.score,
|
||||||
)
|
};
|
||||||
.await?;
|
|
||||||
check_community_deleted_or_removed(post.community_id, &mut context.pool()).await?;
|
|
||||||
|
|
||||||
let like_form = PostLikeForm {
|
// Remove any likes first
|
||||||
post_id: data.post_id,
|
let person_id = local_user_view.person.id;
|
||||||
person_id: local_user_view.person.id,
|
|
||||||
score: data.score,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove any likes first
|
PostLike::remove(&mut context.pool(), person_id, post_id).await?;
|
||||||
let person_id = local_user_view.person.id;
|
|
||||||
|
|
||||||
PostLike::remove(&mut context.pool(), person_id, post_id).await?;
|
// Only add the like if the score isnt 0
|
||||||
|
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
||||||
// Only add the like if the score isnt 0
|
if do_add {
|
||||||
let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1);
|
PostLike::like(&mut context.pool(), &like_form)
|
||||||
if do_add {
|
.await
|
||||||
PostLike::like(&mut context.pool(), &like_form)
|
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntLikePost)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Mark the post as read
|
|
||||||
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
|
||||||
|
|
||||||
build_post_response(
|
|
||||||
context,
|
|
||||||
post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark the post as read
|
||||||
|
mark_post_as_read(person_id, post_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::LikePostOrComment(
|
||||||
|
post.ap_id,
|
||||||
|
local_user_view.person.clone(),
|
||||||
|
Community::read(&mut context.pool(), post.community_id).await?,
|
||||||
|
data.score,
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
build_post_response(
|
||||||
|
context.deref(),
|
||||||
|
post.community_id,
|
||||||
|
local_user_view.person.id,
|
||||||
|
post_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::Perform;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{LockPost, PostResponse},
|
post::{LockPost, PostResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
check_community_deleted_or_removed,
|
check_community_deleted_or_removed,
|
||||||
|
@ -20,58 +21,56 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for LockPost {
|
pub async fn lock_post(
|
||||||
type Response = PostResponse;
|
data: Json<LockPost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let post_id = data.post_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
let data: &LockPost = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
check_community_ban(
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
local_user_view.person.id,
|
||||||
|
orig_post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Verify that only the mods can lock
|
||||||
local_user_view.person.id,
|
is_mod_or_admin(
|
||||||
orig_post.community_id,
|
&mut context.pool(),
|
||||||
&mut context.pool(),
|
local_user_view.person.id,
|
||||||
)
|
orig_post.community_id,
|
||||||
.await?;
|
)
|
||||||
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
.await?;
|
||||||
|
|
||||||
// Verify that only the mods can lock
|
// Update the post
|
||||||
is_mod_or_admin(
|
let post_id = data.post_id;
|
||||||
&mut context.pool(),
|
let locked = data.locked;
|
||||||
local_user_view.person.id,
|
let post = Post::update(
|
||||||
orig_post.community_id,
|
&mut context.pool(),
|
||||||
)
|
post_id,
|
||||||
.await?;
|
&PostUpdateForm::builder().locked(Some(locked)).build(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Update the post
|
// Mod tables
|
||||||
let post_id = data.post_id;
|
let form = ModLockPostForm {
|
||||||
let locked = data.locked;
|
mod_person_id: local_user_view.person.id,
|
||||||
Post::update(
|
post_id: data.post_id,
|
||||||
&mut context.pool(),
|
locked: Some(locked),
|
||||||
post_id,
|
};
|
||||||
&PostUpdateForm::builder().locked(Some(locked)).build(),
|
ModLockPost::create(&mut context.pool(), &form).await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Mod tables
|
let person_id = local_user_view.person.id;
|
||||||
let form = ModLockPostForm {
|
ActivityChannel::submit_activity(
|
||||||
mod_person_id: local_user_view.person.id,
|
SendActivityData::LockPost(post, local_user_view.person, data.locked),
|
||||||
post_id: data.post_id,
|
&context,
|
||||||
locked: Some(locked),
|
)
|
||||||
};
|
.await?;
|
||||||
ModLockPost::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
build_post_response(
|
build_post_response(&context, orig_post.community_id, person_id, post_id).await
|
||||||
context,
|
|
||||||
orig_post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod feature;
|
pub mod feature;
|
||||||
mod get_link_metadata;
|
pub mod get_link_metadata;
|
||||||
mod like;
|
pub mod like;
|
||||||
mod lock;
|
pub mod lock;
|
||||||
mod mark_read;
|
pub mod mark_read;
|
||||||
mod save;
|
pub mod save;
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
use crate::{check_report_reason, Perform};
|
use crate::check_report_reason;
|
||||||
use actix_web::web::Data;
|
use activitypub_federation::config::Data;
|
||||||
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{CreatePostReport, PostReportResponse},
|
post::{CreatePostReport, PostReportResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
local_user_view_from_jwt,
|
local_user_view_from_jwt,
|
||||||
|
@ -21,51 +23,59 @@ use lemmy_db_views::structs::{PostReportView, PostView};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
/// Creates a post report and notifies the moderators of the community
|
/// Creates a post report and notifies the moderators of the community
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl Perform for CreatePostReport {
|
pub async fn create_post_report(
|
||||||
type Response = PostReportResponse;
|
data: Json<CreatePostReport>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostReportResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let reason = sanitize_html(data.reason.trim());
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostReportResponse, LemmyError> {
|
check_report_reason(&reason, &local_site)?;
|
||||||
let data: &CreatePostReport = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let reason = sanitize_html(self.reason.trim());
|
let person_id = local_user_view.person.id;
|
||||||
check_report_reason(&reason, &local_site)?;
|
let post_id = data.post_id;
|
||||||
|
let post_view = PostView::read(&mut context.pool(), post_id, None, None).await?;
|
||||||
|
|
||||||
let person_id = local_user_view.person.id;
|
check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?;
|
||||||
let post_id = data.post_id;
|
|
||||||
let post_view = PostView::read(&mut context.pool(), post_id, None, None).await?;
|
|
||||||
|
|
||||||
check_community_ban(person_id, post_view.community.id, &mut context.pool()).await?;
|
let report_form = PostReportForm {
|
||||||
|
creator_id: person_id,
|
||||||
|
post_id,
|
||||||
|
original_post_name: post_view.post.name,
|
||||||
|
original_post_url: post_view.post.url,
|
||||||
|
original_post_body: post_view.post.body,
|
||||||
|
reason,
|
||||||
|
};
|
||||||
|
|
||||||
let report_form = PostReportForm {
|
let report = PostReport::report(&mut context.pool(), &report_form)
|
||||||
creator_id: person_id,
|
.await
|
||||||
post_id,
|
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
|
||||||
original_post_name: post_view.post.name,
|
|
||||||
original_post_url: post_view.post.url,
|
|
||||||
original_post_body: post_view.post.body,
|
|
||||||
reason,
|
|
||||||
};
|
|
||||||
|
|
||||||
let report = PostReport::report(&mut context.pool(), &report_form)
|
let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?;
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreateReport)?;
|
|
||||||
|
|
||||||
let post_report_view = PostReportView::read(&mut context.pool(), report.id, person_id).await?;
|
// Email the admins
|
||||||
|
if local_site.reports_email_admins {
|
||||||
// Email the admins
|
send_new_report_email_to_admins(
|
||||||
if local_site.reports_email_admins {
|
&post_report_view.creator.name,
|
||||||
send_new_report_email_to_admins(
|
&post_report_view.post_creator.name,
|
||||||
&post_report_view.creator.name,
|
&mut context.pool(),
|
||||||
&post_report_view.post_creator.name,
|
context.settings(),
|
||||||
&mut context.pool(),
|
)
|
||||||
context.settings(),
|
.await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PostReportResponse { post_report_view })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::CreateReport(
|
||||||
|
post_view.post.ap_id.inner().clone(),
|
||||||
|
local_user_view.person,
|
||||||
|
post_view.community,
|
||||||
|
data.reason.clone(),
|
||||||
|
),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(PostReportResponse { post_report_view }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
mod create;
|
pub mod create;
|
||||||
mod list;
|
pub mod list;
|
||||||
mod resolve;
|
pub mod resolve;
|
||||||
|
|
|
@ -5,7 +5,7 @@ use crate::{
|
||||||
post::PostResponse,
|
post::PostResponse,
|
||||||
utils::{check_person_block, get_interface_language, is_mod_or_admin, send_email_to_user},
|
utils::{check_person_block, get_interface_language, is_mod_or_admin, send_email_to_user},
|
||||||
};
|
};
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId},
|
||||||
source::{
|
source::{
|
||||||
|
@ -39,10 +39,10 @@ pub async fn build_comment_response(
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build_community_response(
|
pub async fn build_community_response(
|
||||||
context: &Data<LemmyContext>,
|
context: &LemmyContext,
|
||||||
local_user_view: LocalUserView,
|
local_user_view: LocalUserView,
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
) -> Result<CommunityResponse, LemmyError> {
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
let is_mod_or_admin =
|
let is_mod_or_admin =
|
||||||
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id)
|
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id)
|
||||||
.await
|
.await
|
||||||
|
@ -57,10 +57,10 @@ pub async fn build_community_response(
|
||||||
.await?;
|
.await?;
|
||||||
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
|
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
|
||||||
|
|
||||||
Ok(CommunityResponse {
|
Ok(Json(CommunityResponse {
|
||||||
community_view,
|
community_view,
|
||||||
discussion_languages,
|
discussion_languages,
|
||||||
})
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn build_post_response(
|
pub async fn build_post_response(
|
||||||
|
@ -68,7 +68,7 @@ pub async fn build_post_response(
|
||||||
community_id: CommunityId,
|
community_id: CommunityId,
|
||||||
person_id: PersonId,
|
person_id: PersonId,
|
||||||
post_id: PostId,
|
post_id: PostId,
|
||||||
) -> Result<PostResponse, LemmyError> {
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person_id, community_id)
|
let is_mod_or_admin = is_mod_or_admin(&mut context.pool(), person_id, community_id)
|
||||||
.await
|
.await
|
||||||
.is_ok();
|
.is_ok();
|
||||||
|
@ -79,7 +79,7 @@ pub async fn build_post_response(
|
||||||
Some(is_mod_or_admin),
|
Some(is_mod_or_admin),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
Ok(PostResponse { post_view })
|
Ok(Json(PostResponse { post_view }))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: this function is a mess and should be split up to handle email seperately
|
// TODO: this function is a mess and should be split up to handle email seperately
|
||||||
|
|
|
@ -1,7 +1,22 @@
|
||||||
use crate::context::LemmyContext;
|
use crate::{
|
||||||
|
community::BanFromCommunity,
|
||||||
|
context::LemmyContext,
|
||||||
|
person::BanPerson,
|
||||||
|
post::{DeletePost, RemovePost},
|
||||||
|
};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use futures::future::BoxFuture;
|
use futures::future::BoxFuture;
|
||||||
use lemmy_db_schema::source::{comment::Comment, post::Post};
|
use lemmy_db_schema::{
|
||||||
|
newtypes::{CommunityId, DbUrl, PersonId},
|
||||||
|
source::{
|
||||||
|
comment::Comment,
|
||||||
|
community::Community,
|
||||||
|
person::Person,
|
||||||
|
post::Post,
|
||||||
|
private_message::PrivateMessage,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use lemmy_db_views::structs::PrivateMessageView;
|
||||||
use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
|
use lemmy_utils::{error::LemmyResult, SYNCHRONOUS_FEDERATION};
|
||||||
use once_cell::sync::{Lazy, OnceCell};
|
use once_cell::sync::{Lazy, OnceCell};
|
||||||
use tokio::{
|
use tokio::{
|
||||||
|
@ -12,6 +27,7 @@ use tokio::{
|
||||||
},
|
},
|
||||||
task::JoinHandle,
|
task::JoinHandle,
|
||||||
};
|
};
|
||||||
|
use url::Url;
|
||||||
|
|
||||||
type MatchOutgoingActivitiesBoxed =
|
type MatchOutgoingActivitiesBoxed =
|
||||||
Box<for<'a> fn(SendActivityData, &'a Data<LemmyContext>) -> BoxFuture<'a, LemmyResult<()>>>;
|
Box<for<'a> fn(SendActivityData, &'a Data<LemmyContext>) -> BoxFuture<'a, LemmyResult<()>>>;
|
||||||
|
@ -22,7 +38,28 @@ pub static MATCH_OUTGOING_ACTIVITIES: OnceCell<MatchOutgoingActivitiesBoxed> = O
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum SendActivityData {
|
pub enum SendActivityData {
|
||||||
CreatePost(Post),
|
CreatePost(Post),
|
||||||
|
UpdatePost(Post),
|
||||||
|
DeletePost(Post, Person, DeletePost),
|
||||||
|
RemovePost(Post, Person, RemovePost),
|
||||||
|
LockPost(Post, Person, bool),
|
||||||
|
FeaturePost(Post, Person, bool),
|
||||||
CreateComment(Comment),
|
CreateComment(Comment),
|
||||||
|
UpdateComment(Comment),
|
||||||
|
DeleteComment(Comment, Person, Community),
|
||||||
|
RemoveComment(Comment, Person, Community, Option<String>),
|
||||||
|
LikePostOrComment(DbUrl, Person, Community, i16),
|
||||||
|
FollowCommunity(Community, Person, bool),
|
||||||
|
UpdateCommunity(Person, Community),
|
||||||
|
DeleteCommunity(Person, Community, bool),
|
||||||
|
RemoveCommunity(Person, Community, Option<String>, bool),
|
||||||
|
AddModToCommunity(Person, CommunityId, PersonId, bool),
|
||||||
|
BanFromCommunity(Person, CommunityId, Person, BanFromCommunity),
|
||||||
|
BanFromSite(Person, Person, BanPerson),
|
||||||
|
CreatePrivateMessage(PrivateMessageView),
|
||||||
|
UpdatePrivateMessage(PrivateMessageView),
|
||||||
|
DeletePrivateMessage(Person, PrivateMessage, bool),
|
||||||
|
DeleteUser(Person),
|
||||||
|
CreateReport(Url, Person, Community, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
|
// TODO: instead of static, move this into LemmyContext. make sure that stopping the process with
|
||||||
|
|
|
@ -342,9 +342,8 @@ pub async fn send_password_reset_email(
|
||||||
let token = uuid::Uuid::new_v4().to_string();
|
let token = uuid::Uuid::new_v4().to_string();
|
||||||
|
|
||||||
// Insert the row
|
// Insert the row
|
||||||
let token2 = token.clone();
|
|
||||||
let local_user_id = user.local_user.id;
|
let local_user_id = user.local_user.id;
|
||||||
PasswordResetRequest::create_token(pool, local_user_id, &token2).await?;
|
PasswordResetRequest::create_token(pool, local_user_id, token.clone()).await?;
|
||||||
|
|
||||||
let email = &user.local_user.email.clone().expect("email");
|
let email = &user.local_user.email.clone().expect("email");
|
||||||
let lang = get_interface_language(user);
|
let lang = get_interface_language(user);
|
||||||
|
|
|
@ -36,7 +36,6 @@ use lemmy_utils::{
|
||||||
validation::is_valid_body_field,
|
validation::is_valid_body_field,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
|
const MAX_COMMENT_DEPTH_LIMIT: usize = 100;
|
||||||
|
|
||||||
|
@ -196,7 +195,7 @@ pub async fn create_comment(
|
||||||
|
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
build_comment_response(
|
build_comment_response(
|
||||||
context.deref(),
|
&context,
|
||||||
inserted_comment.id,
|
inserted_comment.id,
|
||||||
Some(local_user_view),
|
Some(local_user_view),
|
||||||
data.form_id.clone(),
|
data.form_id.clone(),
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::{build_comment_response, send_local_notifs},
|
build_response::{build_comment_response, send_local_notifs},
|
||||||
comment::{CommentResponse, DeleteComment},
|
comment::{CommentResponse, DeleteComment},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, local_user_view_from_jwt},
|
utils::{check_community_ban, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,66 +16,75 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::CommentView;
|
use lemmy_db_views::structs::CommentView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeleteComment {
|
pub async fn delete_comment(
|
||||||
type Response = CommentResponse;
|
data: Json<DeleteComment>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommentResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let comment_id = data.comment_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
|
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
||||||
let data: &DeleteComment = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
// Dont delete it if its already been deleted.
|
||||||
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
if orig_comment.comment.deleted == data.deleted {
|
||||||
|
return Err(LemmyErrorType::CouldntUpdateComment)?;
|
||||||
|
}
|
||||||
|
|
||||||
// Dont delete it if its already been deleted.
|
check_community_ban(
|
||||||
if orig_comment.comment.deleted == data.deleted {
|
local_user_view.person.id,
|
||||||
return Err(LemmyErrorType::CouldntUpdateComment)?;
|
orig_comment.community.id,
|
||||||
}
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Verify that only the creator can delete
|
||||||
local_user_view.person.id,
|
if local_user_view.person.id != orig_comment.creator.id {
|
||||||
orig_comment.community.id,
|
return Err(LemmyErrorType::NoCommentEditAllowed)?;
|
||||||
&mut context.pool(),
|
}
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Verify that only the creator can delete
|
// Do the delete
|
||||||
if local_user_view.person.id != orig_comment.creator.id {
|
let deleted = data.deleted;
|
||||||
return Err(LemmyErrorType::NoCommentEditAllowed)?;
|
let updated_comment = Comment::update(
|
||||||
}
|
&mut context.pool(),
|
||||||
|
comment_id,
|
||||||
|
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
||||||
|
|
||||||
// Do the delete
|
let post_id = updated_comment.post_id;
|
||||||
let deleted = data.deleted;
|
let post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
let updated_comment = Comment::update(
|
let recipient_ids = send_local_notifs(
|
||||||
&mut context.pool(),
|
vec![],
|
||||||
comment_id,
|
&updated_comment,
|
||||||
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
|
&local_user_view.person,
|
||||||
)
|
&post,
|
||||||
.await
|
false,
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let updated_comment_id = updated_comment.id;
|
||||||
|
|
||||||
let post_id = updated_comment.post_id;
|
ActivityChannel::submit_activity(
|
||||||
let post = Post::read(&mut context.pool(), post_id).await?;
|
SendActivityData::DeleteComment(
|
||||||
let recipient_ids = send_local_notifs(
|
updated_comment,
|
||||||
vec![],
|
local_user_view.person.clone(),
|
||||||
&updated_comment,
|
orig_comment.community,
|
||||||
&local_user_view.person,
|
),
|
||||||
&post,
|
&context,
|
||||||
false,
|
)
|
||||||
context,
|
.await?;
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
build_comment_response(
|
build_comment_response(
|
||||||
context.deref(),
|
&context,
|
||||||
updated_comment.id,
|
updated_comment_id,
|
||||||
Some(local_user_view),
|
Some(local_user_view),
|
||||||
None,
|
None,
|
||||||
recipient_ids,
|
recipient_ids,
|
||||||
)
|
)
|
||||||
.await
|
.await?,
|
||||||
}
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ use lemmy_api_common::{
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::local_site::LocalSite;
|
use lemmy_db_schema::source::local_site::LocalSite;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
#[tracing::instrument(skip(context))]
|
||||||
pub async fn get_comment(
|
pub async fn get_comment(
|
||||||
|
@ -20,6 +19,6 @@ pub async fn get_comment(
|
||||||
check_private_instance(&local_user_view, &local_site)?;
|
check_private_instance(&local_user_view, &local_site)?;
|
||||||
|
|
||||||
Ok(Json(
|
Ok(Json(
|
||||||
build_comment_response(context.deref(), data.id, local_user_view, None, vec![]).await?,
|
build_comment_response(&context, data.id, local_user_view, None, vec![]).await?,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::{build_comment_response, send_local_notifs},
|
build_response::{build_comment_response, send_local_notifs},
|
||||||
comment::{CommentResponse, RemoveComment},
|
comment::{CommentResponse, RemoveComment},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
|
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -16,73 +17,83 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::CommentView;
|
use lemmy_db_views::structs::CommentView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for RemoveComment {
|
pub async fn remove_comment(
|
||||||
type Response = CommentResponse;
|
data: Json<RemoveComment>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommentResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let comment_id = data.comment_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
|
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
||||||
let data: &RemoveComment = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
check_community_ban(
|
||||||
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
local_user_view.person.id,
|
||||||
|
orig_comment.community.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Verify that only a mod or admin can remove
|
||||||
local_user_view.person.id,
|
is_mod_or_admin(
|
||||||
orig_comment.community.id,
|
&mut context.pool(),
|
||||||
&mut context.pool(),
|
local_user_view.person.id,
|
||||||
)
|
orig_comment.community.id,
|
||||||
.await?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Verify that only a mod or admin can remove
|
// Do the remove
|
||||||
is_mod_or_admin(
|
let removed = data.removed;
|
||||||
&mut context.pool(),
|
let updated_comment = Comment::update(
|
||||||
local_user_view.person.id,
|
&mut context.pool(),
|
||||||
orig_comment.community.id,
|
comment_id,
|
||||||
)
|
&CommentUpdateForm::builder().removed(Some(removed)).build(),
|
||||||
.await?;
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
||||||
|
|
||||||
// Do the remove
|
// Mod tables
|
||||||
let removed = data.removed;
|
let form = ModRemoveCommentForm {
|
||||||
let updated_comment = Comment::update(
|
mod_person_id: local_user_view.person.id,
|
||||||
&mut context.pool(),
|
comment_id: data.comment_id,
|
||||||
comment_id,
|
removed: Some(removed),
|
||||||
&CommentUpdateForm::builder().removed(Some(removed)).build(),
|
reason: data.reason.clone(),
|
||||||
)
|
};
|
||||||
.await
|
ModRemoveComment::create(&mut context.pool(), &form).await?;
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
|
||||||
|
|
||||||
// Mod tables
|
let post_id = updated_comment.post_id;
|
||||||
let form = ModRemoveCommentForm {
|
let post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
mod_person_id: local_user_view.person.id,
|
let recipient_ids = send_local_notifs(
|
||||||
comment_id: data.comment_id,
|
vec![],
|
||||||
removed: Some(removed),
|
&updated_comment,
|
||||||
reason: data.reason.clone(),
|
&local_user_view.person.clone(),
|
||||||
};
|
&post,
|
||||||
ModRemoveComment::create(&mut context.pool(), &form).await?;
|
false,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let updated_comment_id = updated_comment.id;
|
||||||
|
|
||||||
let post_id = updated_comment.post_id;
|
ActivityChannel::submit_activity(
|
||||||
let post = Post::read(&mut context.pool(), post_id).await?;
|
SendActivityData::RemoveComment(
|
||||||
let recipient_ids = send_local_notifs(
|
updated_comment,
|
||||||
vec![],
|
local_user_view.person.clone(),
|
||||||
&updated_comment,
|
orig_comment.community,
|
||||||
&local_user_view.person.clone(),
|
data.reason.clone(),
|
||||||
&post,
|
),
|
||||||
false,
|
&context,
|
||||||
context,
|
)
|
||||||
)
|
.await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
build_comment_response(
|
build_comment_response(
|
||||||
context.deref(),
|
&context,
|
||||||
updated_comment.id,
|
updated_comment_id,
|
||||||
Some(local_user_view),
|
Some(local_user_view),
|
||||||
None,
|
None,
|
||||||
recipient_ids,
|
recipient_ids,
|
||||||
)
|
)
|
||||||
.await
|
.await?,
|
||||||
}
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::{build_comment_response, send_local_notifs},
|
build_response::{build_comment_response, send_local_notifs},
|
||||||
comment::{CommentResponse, EditComment},
|
comment::{CommentResponse, EditComment},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
|
@ -29,79 +30,83 @@ use lemmy_utils::{
|
||||||
validation::is_valid_body_field,
|
validation::is_valid_body_field,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::ops::Deref;
|
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for EditComment {
|
pub async fn update_comment(
|
||||||
type Response = CommentResponse;
|
data: Json<EditComment>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommentResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let comment_id = data.comment_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> {
|
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
||||||
let data: &EditComment = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
check_community_ban(
|
||||||
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
|
local_user_view.person.id,
|
||||||
|
orig_comment.community.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Verify that only the creator can edit
|
||||||
local_user_view.person.id,
|
if local_user_view.person.id != orig_comment.creator.id {
|
||||||
orig_comment.community.id,
|
return Err(LemmyErrorType::NoCommentEditAllowed)?;
|
||||||
&mut context.pool(),
|
}
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Verify that only the creator can edit
|
let language_id = data.language_id;
|
||||||
if local_user_view.person.id != orig_comment.creator.id {
|
CommunityLanguage::is_allowed_community_language(
|
||||||
return Err(LemmyErrorType::NoCommentEditAllowed)?;
|
&mut context.pool(),
|
||||||
}
|
language_id,
|
||||||
|
orig_comment.community.id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
let language_id = self.language_id;
|
// Update the Content
|
||||||
CommunityLanguage::is_allowed_community_language(
|
let content = data
|
||||||
&mut context.pool(),
|
.content
|
||||||
language_id,
|
.as_ref()
|
||||||
orig_comment.community.id,
|
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
|
||||||
)
|
is_valid_body_field(&content, false)?;
|
||||||
.await?;
|
let content = sanitize_html_opt(&content);
|
||||||
|
|
||||||
// Update the Content
|
let comment_id = data.comment_id;
|
||||||
let content = data
|
let form = CommentUpdateForm::builder()
|
||||||
.content
|
.content(content)
|
||||||
.as_ref()
|
.language_id(data.language_id)
|
||||||
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
|
.updated(Some(Some(naive_now())))
|
||||||
is_valid_body_field(&content, false)?;
|
.build();
|
||||||
let content = sanitize_html_opt(&content);
|
let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
||||||
|
|
||||||
let comment_id = data.comment_id;
|
// Do the mentions / recipients
|
||||||
let form = CommentUpdateForm::builder()
|
let updated_comment_content = updated_comment.content.clone();
|
||||||
.content(content)
|
let mentions = scrape_text_for_mentions(&updated_comment_content);
|
||||||
.language_id(data.language_id)
|
let recipient_ids = send_local_notifs(
|
||||||
.updated(Some(Some(naive_now())))
|
mentions,
|
||||||
.build();
|
&updated_comment,
|
||||||
let updated_comment = Comment::update(&mut context.pool(), comment_id, &form)
|
&local_user_view.person,
|
||||||
.await
|
&orig_comment.post,
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateComment)?;
|
false,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Do the mentions / recipients
|
ActivityChannel::submit_activity(
|
||||||
let updated_comment_content = updated_comment.content.clone();
|
SendActivityData::UpdateComment(updated_comment.clone()),
|
||||||
let mentions = scrape_text_for_mentions(&updated_comment_content);
|
&context,
|
||||||
let recipient_ids = send_local_notifs(
|
)
|
||||||
mentions,
|
.await?;
|
||||||
&updated_comment,
|
|
||||||
&local_user_view.person,
|
|
||||||
&orig_comment.post,
|
|
||||||
false,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
|
Ok(Json(
|
||||||
build_comment_response(
|
build_comment_response(
|
||||||
context.deref(),
|
&context,
|
||||||
updated_comment.id,
|
updated_comment.id,
|
||||||
Some(local_user_view),
|
Some(local_user_view),
|
||||||
self.form_id.clone(),
|
data.form_id.clone(),
|
||||||
recipient_ids,
|
recipient_ids,
|
||||||
)
|
)
|
||||||
.await
|
.await?,
|
||||||
}
|
))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair};
|
||||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
use actix_web::web::Json;
|
||||||
use actix_web::web::Data;
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, CreateCommunity},
|
community::{CommunityResponse, CreateCommunity},
|
||||||
|
@ -42,107 +41,104 @@ use lemmy_utils::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for CreateCommunity {
|
pub async fn create_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<CreateCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||||
|
let local_site = site_view.local_site;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?;
|
||||||
let data: &CreateCommunity = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
|
||||||
let local_site = site_view.local_site;
|
|
||||||
|
|
||||||
if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
|
|
||||||
return Err(LemmyErrorType::OnlyAdminsCanCreateCommunities)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check to make sure the icon and banners are urls
|
|
||||||
let icon = diesel_option_overwrite_to_url_create(&data.icon)?;
|
|
||||||
let banner = diesel_option_overwrite_to_url_create(&data.banner)?;
|
|
||||||
|
|
||||||
let name = sanitize_html(&data.name);
|
|
||||||
let title = sanitize_html(&data.title);
|
|
||||||
let description = sanitize_html_opt(&data.description);
|
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
|
||||||
check_slurs(&name, &slur_regex)?;
|
|
||||||
check_slurs(&title, &slur_regex)?;
|
|
||||||
check_slurs_opt(&description, &slur_regex)?;
|
|
||||||
|
|
||||||
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
|
|
||||||
is_valid_body_field(&data.description, false)?;
|
|
||||||
|
|
||||||
// Double check for duplicate community actor_ids
|
|
||||||
let community_actor_id = generate_local_apub_endpoint(
|
|
||||||
EndpointType::Community,
|
|
||||||
&data.name,
|
|
||||||
&context.settings().get_protocol_and_hostname(),
|
|
||||||
)?;
|
|
||||||
let community_dupe =
|
|
||||||
Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?;
|
|
||||||
if community_dupe.is_some() {
|
|
||||||
return Err(LemmyErrorType::CommunityAlreadyExists)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When you create a community, make sure the user becomes a moderator and a follower
|
|
||||||
let keypair = generate_actor_keypair()?;
|
|
||||||
|
|
||||||
let community_form = CommunityInsertForm::builder()
|
|
||||||
.name(name)
|
|
||||||
.title(title)
|
|
||||||
.description(description)
|
|
||||||
.icon(icon)
|
|
||||||
.banner(banner)
|
|
||||||
.nsfw(data.nsfw)
|
|
||||||
.actor_id(Some(community_actor_id.clone()))
|
|
||||||
.private_key(Some(keypair.private_key))
|
|
||||||
.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)?))
|
|
||||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
|
||||||
.instance_id(site_view.site.instance_id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_community = Community::create(&mut context.pool(), &community_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?;
|
|
||||||
|
|
||||||
// The community creator becomes a moderator
|
|
||||||
let community_moderator_form = CommunityModeratorForm {
|
|
||||||
community_id: inserted_community.id,
|
|
||||||
person_id: local_user_view.person.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityModerator::join(&mut context.pool(), &community_moderator_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
|
||||||
|
|
||||||
// Follow your own community
|
|
||||||
let community_follower_form = CommunityFollowerForm {
|
|
||||||
community_id: inserted_community.id,
|
|
||||||
person_id: local_user_view.person.id,
|
|
||||||
pending: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
|
||||||
|
|
||||||
// Update the discussion_languages if that's provided
|
|
||||||
let community_id = inserted_community.id;
|
|
||||||
if let Some(languages) = data.discussion_languages.clone() {
|
|
||||||
let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
|
||||||
// check that community languages are a subset of site languages
|
|
||||||
// https://stackoverflow.com/a/64227550
|
|
||||||
let is_subset = languages.iter().all(|item| site_languages.contains(item));
|
|
||||||
if !is_subset {
|
|
||||||
return Err(LemmyErrorType::LanguageNotAllowed)?;
|
|
||||||
}
|
|
||||||
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
build_community_response(context, local_user_view, community_id).await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check to make sure the icon and banners are urls
|
||||||
|
let icon = diesel_option_overwrite_to_url_create(&data.icon)?;
|
||||||
|
let banner = diesel_option_overwrite_to_url_create(&data.banner)?;
|
||||||
|
|
||||||
|
let name = sanitize_html(&data.name);
|
||||||
|
let title = sanitize_html(&data.title);
|
||||||
|
let description = sanitize_html_opt(&data.description);
|
||||||
|
|
||||||
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
|
check_slurs(&name, &slur_regex)?;
|
||||||
|
check_slurs(&title, &slur_regex)?;
|
||||||
|
check_slurs_opt(&description, &slur_regex)?;
|
||||||
|
|
||||||
|
is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize)?;
|
||||||
|
is_valid_body_field(&data.description, false)?;
|
||||||
|
|
||||||
|
// Double check for duplicate community actor_ids
|
||||||
|
let community_actor_id = generate_local_apub_endpoint(
|
||||||
|
EndpointType::Community,
|
||||||
|
&data.name,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
|
let community_dupe =
|
||||||
|
Community::read_from_apub_id(&mut context.pool(), &community_actor_id).await?;
|
||||||
|
if community_dupe.is_some() {
|
||||||
|
return Err(LemmyErrorType::CommunityAlreadyExists)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// When you create a community, make sure the user becomes a moderator and a follower
|
||||||
|
let keypair = generate_actor_keypair()?;
|
||||||
|
|
||||||
|
let community_form = CommunityInsertForm::builder()
|
||||||
|
.name(name)
|
||||||
|
.title(title)
|
||||||
|
.description(description)
|
||||||
|
.icon(icon)
|
||||||
|
.banner(banner)
|
||||||
|
.nsfw(data.nsfw)
|
||||||
|
.actor_id(Some(community_actor_id.clone()))
|
||||||
|
.private_key(Some(keypair.private_key))
|
||||||
|
.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)?))
|
||||||
|
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||||
|
.instance_id(site_view.site.instance_id)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let inserted_community = Community::create(&mut context.pool(), &community_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityAlreadyExists)?;
|
||||||
|
|
||||||
|
// The community creator becomes a moderator
|
||||||
|
let community_moderator_form = CommunityModeratorForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
person_id: local_user_view.person.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityModerator::join(&mut context.pool(), &community_moderator_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityModeratorAlreadyExists)?;
|
||||||
|
|
||||||
|
// Follow your own community
|
||||||
|
let community_follower_form = CommunityFollowerForm {
|
||||||
|
community_id: inserted_community.id,
|
||||||
|
person_id: local_user_view.person.id,
|
||||||
|
pending: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
CommunityFollower::follow(&mut context.pool(), &community_follower_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?;
|
||||||
|
|
||||||
|
// Update the discussion_languages if that's provided
|
||||||
|
let community_id = inserted_community.id;
|
||||||
|
if let Some(languages) = data.discussion_languages.clone() {
|
||||||
|
let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||||
|
// check that community languages are a subset of site languages
|
||||||
|
// https://stackoverflow.com/a/64227550
|
||||||
|
let is_subset = languages.iter().all(|item| site_languages.contains(item));
|
||||||
|
if !is_subset {
|
||||||
|
return Err(LemmyErrorType::LanguageNotAllowed)?;
|
||||||
|
}
|
||||||
|
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
build_community_response(&context, local_user_view, community_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, DeleteCommunity},
|
community::{CommunityResponse, DeleteCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{is_top_mod, local_user_view_from_jwt},
|
utils::{is_top_mod, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -13,36 +14,39 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeleteCommunity {
|
pub async fn delete_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<DeleteCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
// Fetch the community mods
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
let community_id = data.community_id;
|
||||||
let data: &DeleteCommunity = self;
|
let community_mods =
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
||||||
|
|
||||||
// Fetch the community mods
|
// Make sure deleter is the top mod
|
||||||
let community_id = data.community_id;
|
is_top_mod(&local_user_view, &community_mods)?;
|
||||||
let community_mods =
|
|
||||||
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
|
|
||||||
|
|
||||||
// Make sure deleter is the top mod
|
// Do the delete
|
||||||
is_top_mod(&local_user_view, &community_mods)?;
|
let community_id = data.community_id;
|
||||||
|
let deleted = data.deleted;
|
||||||
|
let community = Community::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
community_id,
|
||||||
|
&CommunityUpdateForm::builder()
|
||||||
|
.deleted(Some(deleted))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
||||||
|
|
||||||
// Do the delete
|
ActivityChannel::submit_activity(
|
||||||
let community_id = data.community_id;
|
SendActivityData::DeleteCommunity(local_user_view.person.clone(), community, data.deleted),
|
||||||
let deleted = data.deleted;
|
&context,
|
||||||
Community::update(
|
)
|
||||||
&mut context.pool(),
|
.await?;
|
||||||
community_id,
|
|
||||||
&CommunityUpdateForm::builder()
|
|
||||||
.deleted(Some(deleted))
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
|
||||||
|
|
||||||
build_community_response(context, local_user_view, community_id).await
|
build_community_response(&context, local_user_view, community_id).await
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, RemoveCommunity},
|
community::{CommunityResponse, RemoveCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{is_admin, local_user_view_from_jwt},
|
utils::{is_admin, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -18,42 +19,50 @@ use lemmy_utils::{
|
||||||
utils::time::naive_from_unix,
|
utils::time::naive_from_unix,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for RemoveCommunity {
|
pub async fn remove_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<RemoveCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
// Verify its an admin (only an admin can remove a community)
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
is_admin(&local_user_view)?;
|
||||||
let data: &RemoveCommunity = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
// Verify its an admin (only an admin can remove a community)
|
// Do the remove
|
||||||
is_admin(&local_user_view)?;
|
let community_id = data.community_id;
|
||||||
|
let removed = data.removed;
|
||||||
|
let community = Community::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
community_id,
|
||||||
|
&CommunityUpdateForm::builder()
|
||||||
|
.removed(Some(removed))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
||||||
|
|
||||||
// Do the remove
|
// Mod tables
|
||||||
let community_id = data.community_id;
|
let expires = data.expires.map(naive_from_unix);
|
||||||
let removed = data.removed;
|
let form = ModRemoveCommunityForm {
|
||||||
Community::update(
|
mod_person_id: local_user_view.person.id,
|
||||||
&mut context.pool(),
|
community_id: data.community_id,
|
||||||
community_id,
|
removed: Some(removed),
|
||||||
&CommunityUpdateForm::builder()
|
reason: data.reason.clone(),
|
||||||
.removed(Some(removed))
|
expires,
|
||||||
.build(),
|
};
|
||||||
)
|
ModRemoveCommunity::create(&mut context.pool(), &form).await?;
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
|
||||||
|
|
||||||
// Mod tables
|
ActivityChannel::submit_activity(
|
||||||
let expires = data.expires.map(naive_from_unix);
|
SendActivityData::RemoveCommunity(
|
||||||
let form = ModRemoveCommunityForm {
|
local_user_view.person.clone(),
|
||||||
mod_person_id: local_user_view.person.id,
|
community,
|
||||||
community_id: data.community_id,
|
data.reason.clone(),
|
||||||
removed: Some(removed),
|
data.removed,
|
||||||
reason: data.reason.clone(),
|
),
|
||||||
expires,
|
&context,
|
||||||
};
|
)
|
||||||
ModRemoveCommunity::create(&mut context.pool(), &form).await?;
|
.await?;
|
||||||
|
|
||||||
build_community_response(context, local_user_view, community_id).await
|
build_community_response(&context, local_user_view, community_id).await
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_community_response,
|
build_response::build_community_response,
|
||||||
community::{CommunityResponse, EditCommunity},
|
community::{CommunityResponse, EditCommunity},
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_opt},
|
utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html_opt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -22,65 +23,68 @@ use lemmy_utils::{
|
||||||
utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
|
utils::{slurs::check_slurs_opt, validation::is_valid_body_field},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for EditCommunity {
|
pub async fn update_community(
|
||||||
type Response = CommunityResponse;
|
data: Json<EditCommunity>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CommunityResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> {
|
check_slurs_opt(&data.title, &slur_regex)?;
|
||||||
let data: &EditCommunity = self;
|
check_slurs_opt(&data.description, &slur_regex)?;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
is_valid_body_field(&data.description, false)?;
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let title = sanitize_html_opt(&data.title);
|
||||||
check_slurs_opt(&data.title, &slur_regex)?;
|
let description = sanitize_html_opt(&data.description);
|
||||||
check_slurs_opt(&data.description, &slur_regex)?;
|
|
||||||
is_valid_body_field(&data.description, false)?;
|
|
||||||
|
|
||||||
let title = sanitize_html_opt(&data.title);
|
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||||
let description = sanitize_html_opt(&data.description);
|
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||||
|
let description = diesel_option_overwrite(description);
|
||||||
|
|
||||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
// Verify its a mod (only mods can edit it)
|
||||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
let community_id = data.community_id;
|
||||||
let description = diesel_option_overwrite(description);
|
let mods: Vec<PersonId> =
|
||||||
|
CommunityModeratorView::for_community(&mut context.pool(), community_id)
|
||||||
// Verify its a mod (only mods can edit it)
|
|
||||||
let community_id = data.community_id;
|
|
||||||
let mods: Vec<PersonId> =
|
|
||||||
CommunityModeratorView::for_community(&mut context.pool(), community_id)
|
|
||||||
.await
|
|
||||||
.map(|v| v.into_iter().map(|m| m.moderator.id).collect())?;
|
|
||||||
if !mods.contains(&local_user_view.person.id) {
|
|
||||||
return Err(LemmyErrorType::NotAModerator)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
|
||||||
if let Some(languages) = data.discussion_languages.clone() {
|
|
||||||
let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
|
||||||
// check that community languages are a subset of site languages
|
|
||||||
// https://stackoverflow.com/a/64227550
|
|
||||||
let is_subset = languages.iter().all(|item| site_languages.contains(item));
|
|
||||||
if !is_subset {
|
|
||||||
return Err(LemmyErrorType::LanguageNotAllowed)?;
|
|
||||||
}
|
|
||||||
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let community_form = CommunityUpdateForm::builder()
|
|
||||||
.title(title)
|
|
||||||
.description(description)
|
|
||||||
.icon(icon)
|
|
||||||
.banner(banner)
|
|
||||||
.nsfw(data.nsfw)
|
|
||||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
|
||||||
.updated(Some(Some(naive_now())))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let community_id = data.community_id;
|
|
||||||
Community::update(&mut context.pool(), community_id, &community_form)
|
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
.map(|v| v.into_iter().map(|m| m.moderator.id).collect())?;
|
||||||
|
if !mods.contains(&local_user_view.person.id) {
|
||||||
build_community_response(context, local_user_view, community_id).await
|
return Err(LemmyErrorType::NotAModerator)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let community_id = data.community_id;
|
||||||
|
if let Some(languages) = data.discussion_languages.clone() {
|
||||||
|
let site_languages = SiteLanguage::read_local_raw(&mut context.pool()).await?;
|
||||||
|
// check that community languages are a subset of site languages
|
||||||
|
// https://stackoverflow.com/a/64227550
|
||||||
|
let is_subset = languages.iter().all(|item| site_languages.contains(item));
|
||||||
|
if !is_subset {
|
||||||
|
return Err(LemmyErrorType::LanguageNotAllowed)?;
|
||||||
|
}
|
||||||
|
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let community_form = CommunityUpdateForm::builder()
|
||||||
|
.title(title)
|
||||||
|
.description(description)
|
||||||
|
.icon(icon)
|
||||||
|
.banner(banner)
|
||||||
|
.nsfw(data.nsfw)
|
||||||
|
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||||
|
.updated(Some(Some(naive_now())))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let community_id = data.community_id;
|
||||||
|
let community = Community::update(&mut context.pool(), community_id, &community_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
build_community_response(&context, local_user_view, community_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
|
custom_emoji::{CreateCustomEmoji, CustomEmojiResponse},
|
||||||
|
@ -13,41 +13,38 @@ use lemmy_db_schema::source::{
|
||||||
use lemmy_db_views::structs::CustomEmojiView;
|
use lemmy_db_views::structs::CustomEmojiView;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for CreateCustomEmoji {
|
pub async fn create_custom_emoji(
|
||||||
type Response = CustomEmojiResponse;
|
data: Json<CreateCustomEmoji>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CustomEmojiResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CustomEmojiResponse, LemmyError> {
|
// Make sure user is an admin
|
||||||
let data: &CreateCustomEmoji = self;
|
is_admin(&local_user_view)?;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let shortcode = sanitize_html(data.shortcode.to_lowercase().trim());
|
||||||
// Make sure user is an admin
|
let alt_text = sanitize_html(&data.alt_text);
|
||||||
is_admin(&local_user_view)?;
|
let category = sanitize_html(&data.category);
|
||||||
|
|
||||||
let shortcode = sanitize_html(data.shortcode.to_lowercase().trim());
|
let emoji_form = CustomEmojiInsertForm::builder()
|
||||||
let alt_text = sanitize_html(&data.alt_text);
|
.local_site_id(local_site.id)
|
||||||
let category = sanitize_html(&data.category);
|
.shortcode(shortcode)
|
||||||
|
.alt_text(alt_text)
|
||||||
let emoji_form = CustomEmojiInsertForm::builder()
|
.category(category)
|
||||||
.local_site_id(local_site.id)
|
.image_url(data.clone().image_url.into())
|
||||||
.shortcode(shortcode)
|
.build();
|
||||||
.alt_text(alt_text)
|
let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?;
|
||||||
.category(category)
|
let mut keywords = vec![];
|
||||||
.image_url(data.clone().image_url.into())
|
for keyword in &data.keywords {
|
||||||
|
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||||
|
.custom_emoji_id(emoji.id)
|
||||||
|
.keyword(keyword.to_lowercase().trim().to_string())
|
||||||
.build();
|
.build();
|
||||||
let emoji = CustomEmoji::create(&mut context.pool(), &emoji_form).await?;
|
keywords.push(keyword_form);
|
||||||
let mut keywords = vec![];
|
|
||||||
for keyword in &data.keywords {
|
|
||||||
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
|
||||||
.custom_emoji_id(emoji.id)
|
|
||||||
.keyword(keyword.to_lowercase().trim().to_string())
|
|
||||||
.build();
|
|
||||||
keywords.push(keyword_form);
|
|
||||||
}
|
|
||||||
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
|
|
||||||
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
|
|
||||||
Ok(CustomEmojiResponse { custom_emoji: view })
|
|
||||||
}
|
}
|
||||||
|
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
|
||||||
|
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
|
||||||
|
Ok(Json(CustomEmojiResponse { custom_emoji: view }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse},
|
custom_emoji::{DeleteCustomEmoji, DeleteCustomEmojiResponse},
|
||||||
|
@ -8,24 +8,18 @@ use lemmy_api_common::{
|
||||||
use lemmy_db_schema::source::custom_emoji::CustomEmoji;
|
use lemmy_db_schema::source::custom_emoji::CustomEmoji;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeleteCustomEmoji {
|
pub async fn delete_custom_emoji(
|
||||||
type Response = DeleteCustomEmojiResponse;
|
data: Json<DeleteCustomEmoji>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<DeleteCustomEmojiResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
// Make sure user is an admin
|
||||||
async fn perform(
|
is_admin(&local_user_view)?;
|
||||||
&self,
|
CustomEmoji::delete(&mut context.pool(), data.id).await?;
|
||||||
context: &Data<LemmyContext>,
|
Ok(Json(DeleteCustomEmojiResponse {
|
||||||
) -> Result<DeleteCustomEmojiResponse, LemmyError> {
|
id: data.id,
|
||||||
let data: &DeleteCustomEmoji = self;
|
success: true,
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
}))
|
||||||
|
|
||||||
// Make sure user is an admin
|
|
||||||
is_admin(&local_user_view)?;
|
|
||||||
CustomEmoji::delete(&mut context.pool(), data.id).await?;
|
|
||||||
Ok(DeleteCustomEmojiResponse {
|
|
||||||
id: data.id,
|
|
||||||
success: true,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
mod create;
|
pub mod create;
|
||||||
mod delete;
|
pub mod delete;
|
||||||
mod update;
|
pub mod update;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
|
custom_emoji::{CustomEmojiResponse, EditCustomEmoji},
|
||||||
|
@ -13,40 +13,37 @@ use lemmy_db_schema::source::{
|
||||||
use lemmy_db_views::structs::CustomEmojiView;
|
use lemmy_db_views::structs::CustomEmojiView;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for EditCustomEmoji {
|
pub async fn update_custom_emoji(
|
||||||
type Response = CustomEmojiResponse;
|
data: Json<EditCustomEmoji>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<CustomEmojiResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CustomEmojiResponse, LemmyError> {
|
// Make sure user is an admin
|
||||||
let data: &EditCustomEmoji = self;
|
is_admin(&local_user_view)?;
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
let alt_text = sanitize_html(&data.alt_text);
|
||||||
// Make sure user is an admin
|
let category = sanitize_html(&data.category);
|
||||||
is_admin(&local_user_view)?;
|
|
||||||
|
|
||||||
let alt_text = sanitize_html(&data.alt_text);
|
let emoji_form = CustomEmojiUpdateForm::builder()
|
||||||
let category = sanitize_html(&data.category);
|
.local_site_id(local_site.id)
|
||||||
|
.alt_text(alt_text)
|
||||||
let emoji_form = CustomEmojiUpdateForm::builder()
|
.category(category)
|
||||||
.local_site_id(local_site.id)
|
.image_url(data.clone().image_url.into())
|
||||||
.alt_text(alt_text)
|
.build();
|
||||||
.category(category)
|
let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?;
|
||||||
.image_url(data.clone().image_url.into())
|
CustomEmojiKeyword::delete(&mut context.pool(), data.id).await?;
|
||||||
|
let mut keywords = vec![];
|
||||||
|
for keyword in &data.keywords {
|
||||||
|
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
||||||
|
.custom_emoji_id(emoji.id)
|
||||||
|
.keyword(keyword.to_lowercase().trim().to_string())
|
||||||
.build();
|
.build();
|
||||||
let emoji = CustomEmoji::update(&mut context.pool(), data.id, &emoji_form).await?;
|
keywords.push(keyword_form);
|
||||||
CustomEmojiKeyword::delete(&mut context.pool(), data.id).await?;
|
|
||||||
let mut keywords = vec![];
|
|
||||||
for keyword in &data.keywords {
|
|
||||||
let keyword_form = CustomEmojiKeywordInsertForm::builder()
|
|
||||||
.custom_emoji_id(emoji.id)
|
|
||||||
.keyword(keyword.to_lowercase().trim().to_string())
|
|
||||||
.build();
|
|
||||||
keywords.push(keyword_form);
|
|
||||||
}
|
|
||||||
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
|
|
||||||
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
|
|
||||||
Ok(CustomEmojiResponse { custom_emoji: view })
|
|
||||||
}
|
}
|
||||||
|
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
|
||||||
|
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
|
||||||
|
Ok(Json(CustomEmojiResponse { custom_emoji: view }))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,3 @@
|
||||||
use actix_web::web::Data;
|
|
||||||
use lemmy_api_common::context::LemmyContext;
|
|
||||||
use lemmy_utils::error::LemmyError;
|
|
||||||
|
|
||||||
pub mod comment;
|
pub mod comment;
|
||||||
pub mod community;
|
pub mod community;
|
||||||
pub mod custom_emoji;
|
pub mod custom_emoji;
|
||||||
|
@ -9,10 +5,3 @@ pub mod post;
|
||||||
pub mod private_message;
|
pub mod private_message;
|
||||||
pub mod site;
|
pub mod site;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
|
||||||
pub trait PerformCrud {
|
|
||||||
type Response: serde::ser::Serialize + Send + Clone + Sync;
|
|
||||||
|
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError>;
|
|
||||||
}
|
|
||||||
|
|
|
@ -194,7 +194,5 @@ pub async fn create_post(
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok(Json(
|
build_post_response(&context, community_id, person_id, post_id).await
|
||||||
build_post_response(&context, community_id, person_id, post_id).await?,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{DeletePost, PostResponse},
|
post::{DeletePost, PostResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
|
utils::{check_community_ban, check_community_deleted_or_removed, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -12,52 +13,50 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeletePost {
|
pub async fn delete_post(
|
||||||
type Response = PostResponse;
|
data: Json<DeletePost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let post_id = data.post_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
let data: &DeletePost = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
// Dont delete it if its already been deleted.
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
if orig_post.deleted == data.deleted {
|
||||||
|
return Err(LemmyErrorType::CouldntUpdatePost)?;
|
||||||
// Dont delete it if its already been deleted.
|
|
||||||
if orig_post.deleted == data.deleted {
|
|
||||||
return Err(LemmyErrorType::CouldntUpdatePost)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
check_community_ban(
|
|
||||||
local_user_view.person.id,
|
|
||||||
orig_post.community_id,
|
|
||||||
&mut context.pool(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
|
||||||
|
|
||||||
// Verify that only the creator can delete
|
|
||||||
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
|
|
||||||
return Err(LemmyErrorType::NoPostEditAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the post
|
|
||||||
let post_id = data.post_id;
|
|
||||||
let deleted = data.deleted;
|
|
||||||
Post::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
post_id,
|
|
||||||
&PostUpdateForm::builder().deleted(Some(deleted)).build(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
build_post_response(
|
|
||||||
context,
|
|
||||||
orig_post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_community_ban(
|
||||||
|
local_user_view.person.id,
|
||||||
|
orig_post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
check_community_deleted_or_removed(orig_post.community_id, &mut context.pool()).await?;
|
||||||
|
|
||||||
|
// Verify that only the creator can delete
|
||||||
|
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
|
||||||
|
return Err(LemmyErrorType::NoPostEditAllowed)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the post
|
||||||
|
let post = Post::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
data.post_id,
|
||||||
|
&PostUpdateForm::builder()
|
||||||
|
.deleted(Some(data.deleted))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let person_id = local_user_view.person.id;
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::DeletePost(post, local_user_view.person, data.0.clone()),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
build_post_response(&context, orig_post.community_id, person_id, data.post_id).await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{PostResponse, RemovePost},
|
post::{PostResponse, RemovePost},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
|
utils::{check_community_ban, is_mod_or_admin, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -15,58 +16,56 @@ use lemmy_db_schema::{
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for RemovePost {
|
pub async fn remove_post(
|
||||||
type Response = PostResponse;
|
data: Json<RemovePost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let post_id = data.post_id;
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
let data: &RemovePost = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
check_community_ban(
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
local_user_view.person.id,
|
||||||
|
orig_post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_community_ban(
|
// Verify that only the mods can remove
|
||||||
local_user_view.person.id,
|
is_mod_or_admin(
|
||||||
orig_post.community_id,
|
&mut context.pool(),
|
||||||
&mut context.pool(),
|
local_user_view.person.id,
|
||||||
)
|
orig_post.community_id,
|
||||||
.await?;
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Verify that only the mods can remove
|
// Update the post
|
||||||
is_mod_or_admin(
|
let post_id = data.post_id;
|
||||||
&mut context.pool(),
|
let removed = data.removed;
|
||||||
local_user_view.person.id,
|
let post = Post::update(
|
||||||
orig_post.community_id,
|
&mut context.pool(),
|
||||||
)
|
post_id,
|
||||||
.await?;
|
&PostUpdateForm::builder().removed(Some(removed)).build(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
// Update the post
|
// Mod tables
|
||||||
let post_id = data.post_id;
|
let form = ModRemovePostForm {
|
||||||
let removed = data.removed;
|
mod_person_id: local_user_view.person.id,
|
||||||
Post::update(
|
post_id: data.post_id,
|
||||||
&mut context.pool(),
|
removed: Some(removed),
|
||||||
post_id,
|
reason: data.reason.clone(),
|
||||||
&PostUpdateForm::builder().removed(Some(removed)).build(),
|
};
|
||||||
)
|
ModRemovePost::create(&mut context.pool(), &form).await?;
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Mod tables
|
let person_id = local_user_view.person.id;
|
||||||
let form = ModRemovePostForm {
|
ActivityChannel::submit_activity(
|
||||||
mod_person_id: local_user_view.person.id,
|
SendActivityData::RemovePost(post, local_user_view.person, data.0),
|
||||||
post_id: data.post_id,
|
&context,
|
||||||
removed: Some(removed),
|
)
|
||||||
reason: data.reason.clone(),
|
.await?;
|
||||||
};
|
|
||||||
ModRemovePost::create(&mut context.pool(), &form).await?;
|
|
||||||
|
|
||||||
build_post_response(
|
build_post_response(&context, orig_post.community_id, person_id, post_id).await
|
||||||
context,
|
|
||||||
orig_post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::build_post_response,
|
build_response::build_post_response,
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{EditPost, PostResponse},
|
post::{EditPost, PostResponse},
|
||||||
request::fetch_site_data,
|
request::fetch_site_data,
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_community_ban,
|
check_community_ban,
|
||||||
local_site_to_slur_regex,
|
local_site_to_slur_regex,
|
||||||
|
@ -28,95 +29,95 @@ use lemmy_utils::{
|
||||||
validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
|
validation::{check_url_scheme, clean_url_params, is_valid_body_field, is_valid_post_title},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use std::ops::Deref;
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for EditPost {
|
pub async fn update_post(
|
||||||
type Response = PostResponse;
|
data: Json<EditPost>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PostResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(context))]
|
let data_url = data.url.as_ref();
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> {
|
|
||||||
let data: &EditPost = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let data_url = data.url.as_ref();
|
// TODO No good way to handle a clear.
|
||||||
|
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
|
||||||
|
let url = Some(data_url.map(clean_url_params).map(Into::into));
|
||||||
|
|
||||||
// TODO No good way to handle a clear.
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
// Issue link: https://github.com/LemmyNet/lemmy/issues/2287
|
check_slurs_opt(&data.name, &slur_regex)?;
|
||||||
let url = Some(data_url.map(clean_url_params).map(Into::into));
|
check_slurs_opt(&data.body, &slur_regex)?;
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
if let Some(name) = &data.name {
|
||||||
check_slurs_opt(&data.name, &slur_regex)?;
|
is_valid_post_title(name)?;
|
||||||
check_slurs_opt(&data.body, &slur_regex)?;
|
|
||||||
|
|
||||||
if let Some(name) = &data.name {
|
|
||||||
is_valid_post_title(name)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
is_valid_body_field(&data.body, true)?;
|
|
||||||
check_url_scheme(&data.url)?;
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
|
||||||
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
|
||||||
|
|
||||||
check_community_ban(
|
|
||||||
local_user_view.person.id,
|
|
||||||
orig_post.community_id,
|
|
||||||
&mut context.pool(),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
// Verify that only the creator can edit
|
|
||||||
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
|
|
||||||
return Err(LemmyErrorType::NoPostEditAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch post links and Pictrs cached image
|
|
||||||
let data_url = data.url.as_ref();
|
|
||||||
let (metadata_res, thumbnail_url) =
|
|
||||||
fetch_site_data(context.client(), context.settings(), data_url, true).await;
|
|
||||||
let (embed_title, embed_description, embed_video_url) = metadata_res
|
|
||||||
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
let name = sanitize_html_opt(&data.name);
|
|
||||||
let body = sanitize_html_opt(&data.body);
|
|
||||||
let body = diesel_option_overwrite(body);
|
|
||||||
let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
|
|
||||||
let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
|
|
||||||
|
|
||||||
let language_id = self.language_id;
|
|
||||||
CommunityLanguage::is_allowed_community_language(
|
|
||||||
&mut context.pool(),
|
|
||||||
language_id,
|
|
||||||
orig_post.community_id,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
let post_form = PostUpdateForm::builder()
|
|
||||||
.name(name)
|
|
||||||
.url(url)
|
|
||||||
.body(body)
|
|
||||||
.nsfw(data.nsfw)
|
|
||||||
.embed_title(embed_title)
|
|
||||||
.embed_description(embed_description)
|
|
||||||
.embed_video_url(embed_video_url)
|
|
||||||
.language_id(data.language_id)
|
|
||||||
.thumbnail_url(Some(thumbnail_url))
|
|
||||||
.updated(Some(Some(naive_now())))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let post_id = data.post_id;
|
|
||||||
Post::update(&mut context.pool(), post_id, &post_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
|
||||||
|
|
||||||
build_post_response(
|
|
||||||
context,
|
|
||||||
orig_post.community_id,
|
|
||||||
local_user_view.person.id,
|
|
||||||
post_id,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is_valid_body_field(&data.body, true)?;
|
||||||
|
check_url_scheme(&data.url)?;
|
||||||
|
|
||||||
|
let post_id = data.post_id;
|
||||||
|
let orig_post = Post::read(&mut context.pool(), post_id).await?;
|
||||||
|
|
||||||
|
check_community_ban(
|
||||||
|
local_user_view.person.id,
|
||||||
|
orig_post.community_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
// Verify that only the creator can edit
|
||||||
|
if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
|
||||||
|
return Err(LemmyErrorType::NoPostEditAllowed)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch post links and Pictrs cached image
|
||||||
|
let data_url = data.url.as_ref();
|
||||||
|
let (metadata_res, thumbnail_url) =
|
||||||
|
fetch_site_data(context.client(), context.settings(), data_url, true).await;
|
||||||
|
let (embed_title, embed_description, embed_video_url) = metadata_res
|
||||||
|
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let name = sanitize_html_opt(&data.name);
|
||||||
|
let body = sanitize_html_opt(&data.body);
|
||||||
|
let body = diesel_option_overwrite(body);
|
||||||
|
let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
|
||||||
|
let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
|
||||||
|
|
||||||
|
let language_id = data.language_id;
|
||||||
|
CommunityLanguage::is_allowed_community_language(
|
||||||
|
&mut context.pool(),
|
||||||
|
language_id,
|
||||||
|
orig_post.community_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let post_form = PostUpdateForm::builder()
|
||||||
|
.name(name)
|
||||||
|
.url(url)
|
||||||
|
.body(body)
|
||||||
|
.nsfw(data.nsfw)
|
||||||
|
.embed_title(embed_title)
|
||||||
|
.embed_description(embed_description)
|
||||||
|
.embed_video_url(embed_video_url)
|
||||||
|
.language_id(data.language_id)
|
||||||
|
.thumbnail_url(Some(thumbnail_url))
|
||||||
|
.updated(Some(Some(naive_now())))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let post_id = data.post_id;
|
||||||
|
let updated_post = Post::update(&mut context.pool(), post_id, &post_form)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?;
|
||||||
|
|
||||||
|
build_post_response(
|
||||||
|
context.deref(),
|
||||||
|
orig_post.community_id,
|
||||||
|
local_user_view.person.id,
|
||||||
|
post_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
private_message::{CreatePrivateMessage, PrivateMessageResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{
|
utils::{
|
||||||
check_person_block,
|
check_person_block,
|
||||||
generate_local_apub_endpoint,
|
generate_local_apub_endpoint,
|
||||||
|
@ -27,78 +28,77 @@ use lemmy_utils::{
|
||||||
utils::{slurs::remove_slurs, validation::is_valid_body_field},
|
utils::{slurs::remove_slurs, validation::is_valid_body_field},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for CreatePrivateMessage {
|
pub async fn create_private_message(
|
||||||
type Response = PrivateMessageResponse;
|
data: Json<CreatePrivateMessage>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PrivateMessageResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
let content = sanitize_html(&data.content);
|
||||||
async fn perform(
|
let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
|
||||||
&self,
|
is_valid_body_field(&Some(content.clone()), false)?;
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
|
||||||
let data: &CreatePrivateMessage = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
let content = sanitize_html(&data.content);
|
check_person_block(
|
||||||
let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
|
local_user_view.person.id,
|
||||||
is_valid_body_field(&Some(content.clone()), false)?;
|
data.recipient_id,
|
||||||
|
&mut context.pool(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
check_person_block(
|
let private_message_form = PrivateMessageInsertForm::builder()
|
||||||
local_user_view.person.id,
|
.content(content.clone())
|
||||||
data.recipient_id,
|
.creator_id(local_user_view.person.id)
|
||||||
&mut context.pool(),
|
.recipient_id(data.recipient_id)
|
||||||
)
|
.build();
|
||||||
.await?;
|
|
||||||
|
|
||||||
let private_message_form = PrivateMessageInsertForm::builder()
|
let inserted_private_message = PrivateMessage::create(&mut context.pool(), &private_message_form)
|
||||||
.content(content.clone())
|
|
||||||
.creator_id(local_user_view.person.id)
|
|
||||||
.recipient_id(data.recipient_id)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let inserted_private_message =
|
|
||||||
PrivateMessage::create(&mut context.pool(), &private_message_form)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
|
||||||
|
|
||||||
let inserted_private_message_id = inserted_private_message.id;
|
|
||||||
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
|
||||||
let apub_id = generate_local_apub_endpoint(
|
|
||||||
EndpointType::PrivateMessage,
|
|
||||||
&inserted_private_message_id.to_string(),
|
|
||||||
&protocol_and_hostname,
|
|
||||||
)?;
|
|
||||||
PrivateMessage::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
inserted_private_message.id,
|
|
||||||
&PrivateMessageUpdateForm::builder()
|
|
||||||
.ap_id(Some(apub_id))
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
||||||
|
|
||||||
let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id).await?;
|
let inserted_private_message_id = inserted_private_message.id;
|
||||||
|
let protocol_and_hostname = context.settings().get_protocol_and_hostname();
|
||||||
|
let apub_id = generate_local_apub_endpoint(
|
||||||
|
EndpointType::PrivateMessage,
|
||||||
|
&inserted_private_message_id.to_string(),
|
||||||
|
&protocol_and_hostname,
|
||||||
|
)?;
|
||||||
|
PrivateMessage::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
inserted_private_message.id,
|
||||||
|
&PrivateMessageUpdateForm::builder()
|
||||||
|
.ap_id(Some(apub_id))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
|
||||||
|
|
||||||
// Send email to the local recipient, if one exists
|
let view = PrivateMessageView::read(&mut context.pool(), inserted_private_message.id).await?;
|
||||||
if view.recipient.local {
|
|
||||||
let recipient_id = data.recipient_id;
|
|
||||||
let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?;
|
|
||||||
let lang = get_interface_language(&local_recipient);
|
|
||||||
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
|
||||||
let sender_name = &local_user_view.person.name;
|
|
||||||
send_email_to_user(
|
|
||||||
&local_recipient,
|
|
||||||
&lang.notification_private_message_subject(sender_name),
|
|
||||||
&lang.notification_private_message_body(inbox_link, &content, sender_name),
|
|
||||||
context.settings(),
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(PrivateMessageResponse {
|
// Send email to the local recipient, if one exists
|
||||||
private_message_view: view,
|
if view.recipient.local {
|
||||||
})
|
let recipient_id = data.recipient_id;
|
||||||
|
let local_recipient = LocalUserView::read_person(&mut context.pool(), recipient_id).await?;
|
||||||
|
let lang = get_interface_language(&local_recipient);
|
||||||
|
let inbox_link = format!("{}/inbox", context.settings().get_protocol_and_hostname());
|
||||||
|
let sender_name = &local_user_view.person.name;
|
||||||
|
send_email_to_user(
|
||||||
|
&local_recipient,
|
||||||
|
&lang.notification_private_message_subject(sender_name),
|
||||||
|
&lang.notification_private_message_body(inbox_link, &content, sender_name),
|
||||||
|
context.settings(),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::CreatePrivateMessage(view.clone()),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(PrivateMessageResponse {
|
||||||
|
private_message_view: view,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
private_message::{DeletePrivateMessage, PrivateMessageResponse},
|
private_message::{DeletePrivateMessage, PrivateMessageResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::local_user_view_from_jwt,
|
utils::local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -12,42 +13,41 @@ use lemmy_db_schema::{
|
||||||
use lemmy_db_views::structs::PrivateMessageView;
|
use lemmy_db_views::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeletePrivateMessage {
|
pub async fn delete_private_message(
|
||||||
type Response = PrivateMessageResponse;
|
data: Json<DeletePrivateMessage>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PrivateMessageResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
// Checking permissions
|
||||||
async fn perform(
|
let private_message_id = data.private_message_id;
|
||||||
&self,
|
let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
|
||||||
context: &Data<LemmyContext>,
|
if local_user_view.person.id != orig_private_message.creator_id {
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
|
||||||
let data: &DeletePrivateMessage = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
|
|
||||||
// Checking permissions
|
|
||||||
let private_message_id = data.private_message_id;
|
|
||||||
let orig_private_message =
|
|
||||||
PrivateMessage::read(&mut context.pool(), private_message_id).await?;
|
|
||||||
if local_user_view.person.id != orig_private_message.creator_id {
|
|
||||||
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing the update
|
|
||||||
let private_message_id = data.private_message_id;
|
|
||||||
let deleted = data.deleted;
|
|
||||||
PrivateMessage::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
private_message_id,
|
|
||||||
&PrivateMessageUpdateForm::builder()
|
|
||||||
.deleted(Some(deleted))
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
|
||||||
|
|
||||||
let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
|
|
||||||
Ok(PrivateMessageResponse {
|
|
||||||
private_message_view: view,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Doing the update
|
||||||
|
let private_message_id = data.private_message_id;
|
||||||
|
let deleted = data.deleted;
|
||||||
|
let private_message = PrivateMessage::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
private_message_id,
|
||||||
|
&PrivateMessageUpdateForm::builder()
|
||||||
|
.deleted(Some(deleted))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::DeletePrivateMessage(local_user_view.person, private_message, data.deleted),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
|
||||||
|
Ok(Json(PrivateMessageResponse {
|
||||||
|
private_message_view: view,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
private_message::{EditPrivateMessage, PrivateMessageResponse},
|
private_message::{EditPrivateMessage, PrivateMessageResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html},
|
utils::{local_site_to_slur_regex, local_user_view_from_jwt, sanitize_html},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
@ -19,48 +20,47 @@ use lemmy_utils::{
|
||||||
utils::{slurs::remove_slurs, validation::is_valid_body_field},
|
utils::{slurs::remove_slurs, validation::is_valid_body_field},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for EditPrivateMessage {
|
pub async fn update_private_message(
|
||||||
type Response = PrivateMessageResponse;
|
data: Json<EditPrivateMessage>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<PrivateMessageResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
|
||||||
|
let local_site = LocalSite::read(&mut context.pool()).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
// Checking permissions
|
||||||
async fn perform(
|
let private_message_id = data.private_message_id;
|
||||||
&self,
|
let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
|
||||||
context: &Data<LemmyContext>,
|
if local_user_view.person.id != orig_private_message.creator_id {
|
||||||
) -> Result<PrivateMessageResponse, LemmyError> {
|
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
|
||||||
let data: &EditPrivateMessage = self;
|
|
||||||
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
|
|
||||||
let local_site = LocalSite::read(&mut context.pool()).await?;
|
|
||||||
|
|
||||||
// Checking permissions
|
|
||||||
let private_message_id = data.private_message_id;
|
|
||||||
let orig_private_message =
|
|
||||||
PrivateMessage::read(&mut context.pool(), private_message_id).await?;
|
|
||||||
if local_user_view.person.id != orig_private_message.creator_id {
|
|
||||||
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Doing the update
|
|
||||||
let content = sanitize_html(&data.content);
|
|
||||||
let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
|
|
||||||
is_valid_body_field(&Some(content.clone()), false)?;
|
|
||||||
|
|
||||||
let private_message_id = data.private_message_id;
|
|
||||||
PrivateMessage::update(
|
|
||||||
&mut context.pool(),
|
|
||||||
private_message_id,
|
|
||||||
&PrivateMessageUpdateForm::builder()
|
|
||||||
.content(Some(content))
|
|
||||||
.updated(Some(Some(naive_now())))
|
|
||||||
.build(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
|
||||||
|
|
||||||
let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
|
|
||||||
|
|
||||||
Ok(PrivateMessageResponse {
|
|
||||||
private_message_view: view,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Doing the update
|
||||||
|
let content = sanitize_html(&data.content);
|
||||||
|
let content = remove_slurs(&content, &local_site_to_slur_regex(&local_site));
|
||||||
|
is_valid_body_field(&Some(content.clone()), false)?;
|
||||||
|
|
||||||
|
let private_message_id = data.private_message_id;
|
||||||
|
PrivateMessage::update(
|
||||||
|
&mut context.pool(),
|
||||||
|
private_message_id,
|
||||||
|
&PrivateMessageUpdateForm::builder()
|
||||||
|
.content(Some(content))
|
||||||
|
.updated(Some(Some(naive_now())))
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?;
|
||||||
|
|
||||||
|
let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::UpdatePrivateMessage(view.clone()),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(PrivateMessageResponse {
|
||||||
|
private_message_view: view,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::{config::Data, http_signatures::generate_actor_keypair};
|
||||||
use activitypub_federation::http_signatures::generate_actor_keypair;
|
use actix_web::web::Json;
|
||||||
use actix_web::web::Data;
|
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::{LoginResponse, Register},
|
person::{LoginResponse, Register},
|
||||||
|
@ -38,177 +37,173 @@ use lemmy_utils::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for Register {
|
pub async fn register(
|
||||||
type Response = LoginResponse;
|
data: Json<Register>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<LoginResponse>, LemmyError> {
|
||||||
|
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
||||||
|
let local_site = site_view.local_site;
|
||||||
|
let require_registration_application =
|
||||||
|
local_site.registration_mode == RegistrationMode::RequireApplication;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
if local_site.registration_mode == RegistrationMode::Closed {
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<LoginResponse, LemmyError> {
|
return Err(LemmyErrorType::RegistrationClosed)?;
|
||||||
let data: &Register = self;
|
}
|
||||||
|
|
||||||
let site_view = SiteView::read_local(&mut context.pool()).await?;
|
password_length_check(&data.password)?;
|
||||||
let local_site = site_view.local_site;
|
honeypot_check(&data.honeypot)?;
|
||||||
let require_registration_application =
|
|
||||||
local_site.registration_mode == RegistrationMode::RequireApplication;
|
|
||||||
|
|
||||||
if local_site.registration_mode == RegistrationMode::Closed {
|
if local_site.require_email_verification && data.email.is_none() {
|
||||||
return Err(LemmyErrorType::RegistrationClosed)?;
|
return Err(LemmyErrorType::EmailRequired)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
password_length_check(&data.password)?;
|
if local_site.site_setup && require_registration_application && data.answer.is_none() {
|
||||||
honeypot_check(&data.honeypot)?;
|
return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?;
|
||||||
|
}
|
||||||
|
|
||||||
if local_site.require_email_verification && data.email.is_none() {
|
// Make sure passwords match
|
||||||
return Err(LemmyErrorType::EmailRequired)?;
|
if data.password != data.password_verify {
|
||||||
}
|
return Err(LemmyErrorType::PasswordsDoNotMatch)?;
|
||||||
|
}
|
||||||
|
|
||||||
if local_site.site_setup && require_registration_application && data.answer.is_none() {
|
if local_site.site_setup && local_site.captcha_enabled {
|
||||||
return Err(LemmyErrorType::RegistrationApplicationAnswerRequired)?;
|
if let Some(captcha_uuid) = &data.captcha_uuid {
|
||||||
}
|
let uuid = uuid::Uuid::parse_str(captcha_uuid)?;
|
||||||
|
let check = CaptchaAnswer::check_captcha(
|
||||||
// Make sure passwords match
|
&mut context.pool(),
|
||||||
if data.password != data.password_verify {
|
CheckCaptchaAnswer {
|
||||||
return Err(LemmyErrorType::PasswordsDoNotMatch)?;
|
uuid,
|
||||||
}
|
answer: data.captcha_answer.clone().unwrap_or_default(),
|
||||||
|
},
|
||||||
if local_site.site_setup && local_site.captcha_enabled {
|
)
|
||||||
if let Some(captcha_uuid) = &data.captcha_uuid {
|
.await?;
|
||||||
let uuid = uuid::Uuid::parse_str(captcha_uuid)?;
|
if !check {
|
||||||
let check = CaptchaAnswer::check_captcha(
|
|
||||||
&mut context.pool(),
|
|
||||||
CheckCaptchaAnswer {
|
|
||||||
uuid,
|
|
||||||
answer: data.captcha_answer.clone().unwrap_or_default(),
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
if !check {
|
|
||||||
return Err(LemmyErrorType::CaptchaIncorrect)?;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return Err(LemmyErrorType::CaptchaIncorrect)?;
|
return Err(LemmyErrorType::CaptchaIncorrect)?;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(LemmyErrorType::CaptchaIncorrect)?;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||||
check_slurs(&data.username, &slur_regex)?;
|
check_slurs(&data.username, &slur_regex)?;
|
||||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||||
let username = sanitize_html(&data.username);
|
let username = sanitize_html(&data.username);
|
||||||
|
|
||||||
let actor_keypair = generate_actor_keypair()?;
|
let actor_keypair = generate_actor_keypair()?;
|
||||||
is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?;
|
is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize)?;
|
||||||
let actor_id = generate_local_apub_endpoint(
|
let actor_id = generate_local_apub_endpoint(
|
||||||
EndpointType::Person,
|
EndpointType::Person,
|
||||||
&data.username,
|
&data.username,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
if let Some(email) = &data.email {
|
if let Some(email) = &data.email {
|
||||||
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
if LocalUser::is_email_taken(&mut context.pool(), email).await? {
|
||||||
return Err(LemmyErrorType::EmailAlreadyExists)?;
|
return Err(LemmyErrorType::EmailAlreadyExists)?;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We have to create both a person, and local_user
|
// We have to create both a person, and local_user
|
||||||
|
|
||||||
// Register the new person
|
// Register the new person
|
||||||
let person_form = PersonInsertForm::builder()
|
let person_form = PersonInsertForm::builder()
|
||||||
.name(username)
|
.name(username)
|
||||||
.actor_id(Some(actor_id.clone()))
|
.actor_id(Some(actor_id.clone()))
|
||||||
.private_key(Some(actor_keypair.private_key))
|
.private_key(Some(actor_keypair.private_key))
|
||||||
.public_key(actor_keypair.public_key)
|
.public_key(actor_keypair.public_key)
|
||||||
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
.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(&actor_id)?))
|
||||||
// If its the initial site setup, they are an admin
|
// If its the initial site setup, they are an admin
|
||||||
.admin(Some(!local_site.site_setup))
|
.admin(Some(!local_site.site_setup))
|
||||||
.instance_id(site_view.site.instance_id)
|
.instance_id(site_view.site.instance_id)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
// insert the person
|
// insert the person
|
||||||
let inserted_person = Person::create(&mut context.pool(), &person_form)
|
let inserted_person = Person::create(&mut context.pool(), &person_form)
|
||||||
.await
|
.await
|
||||||
.with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
|
.with_lemmy_type(LemmyErrorType::UserAlreadyExists)?;
|
||||||
|
|
||||||
// Automatically set their application as accepted, if they created this with open registration.
|
// Automatically set their application as accepted, if they created this with open registration.
|
||||||
// Also fixes a bug which allows users to log in when registrations are changed to closed.
|
// Also fixes a bug which allows users to log in when registrations are changed to closed.
|
||||||
let accepted_application = Some(!require_registration_application);
|
let accepted_application = Some(!require_registration_application);
|
||||||
|
|
||||||
// Create the local user
|
// Create the local user
|
||||||
let local_user_form = LocalUserInsertForm::builder()
|
let local_user_form = LocalUserInsertForm::builder()
|
||||||
.person_id(inserted_person.id)
|
.person_id(inserted_person.id)
|
||||||
.email(data.email.as_deref().map(str::to_lowercase))
|
.email(data.email.as_deref().map(str::to_lowercase))
|
||||||
.password_encrypted(data.password.to_string())
|
.password_encrypted(data.password.to_string())
|
||||||
.show_nsfw(Some(data.show_nsfw))
|
.show_nsfw(Some(data.show_nsfw))
|
||||||
.accepted_application(accepted_application)
|
.accepted_application(accepted_application)
|
||||||
.default_listing_type(Some(local_site.default_post_listing_type))
|
.default_listing_type(Some(local_site.default_post_listing_type))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
|
let inserted_local_user = LocalUser::create(&mut context.pool(), &local_user_form).await?;
|
||||||
|
|
||||||
if local_site.site_setup && require_registration_application {
|
if local_site.site_setup && require_registration_application {
|
||||||
// Create the registration application
|
// Create the registration application
|
||||||
let form = RegistrationApplicationInsertForm {
|
let form = RegistrationApplicationInsertForm {
|
||||||
local_user_id: inserted_local_user.id,
|
local_user_id: inserted_local_user.id,
|
||||||
// We already made sure answer was not null above
|
// We already made sure answer was not null above
|
||||||
answer: data.answer.clone().expect("must have an answer"),
|
answer: data.answer.clone().expect("must have an answer"),
|
||||||
};
|
|
||||||
|
|
||||||
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Email the admins
|
|
||||||
if local_site.application_email_admins {
|
|
||||||
send_new_applicant_email_to_admins(&data.username, &mut context.pool(), context.settings())
|
|
||||||
.await?;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut login_response = LoginResponse {
|
|
||||||
jwt: None,
|
|
||||||
registration_created: false,
|
|
||||||
verify_email_sent: false,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Log the user in directly if the site is not setup, or email verification and application aren't required
|
RegistrationApplication::create(&mut context.pool(), &form).await?;
|
||||||
if !local_site.site_setup
|
}
|
||||||
|| (!require_registration_application && !local_site.require_email_verification)
|
|
||||||
{
|
|
||||||
login_response.jwt = Some(
|
|
||||||
Claims::jwt(
|
|
||||||
inserted_local_user.id.0,
|
|
||||||
&context.secret().jwt_secret,
|
|
||||||
&context.settings().hostname,
|
|
||||||
)?
|
|
||||||
.into(),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
if local_site.require_email_verification {
|
|
||||||
let local_user_view = LocalUserView {
|
|
||||||
local_user: inserted_local_user,
|
|
||||||
person: inserted_person,
|
|
||||||
counts: PersonAggregates::default(),
|
|
||||||
};
|
|
||||||
// we check at the beginning of this method that email is set
|
|
||||||
let email = local_user_view
|
|
||||||
.local_user
|
|
||||||
.email
|
|
||||||
.clone()
|
|
||||||
.expect("email was provided");
|
|
||||||
|
|
||||||
send_verification_email(
|
// Email the admins
|
||||||
&local_user_view,
|
if local_site.application_email_admins {
|
||||||
&email,
|
send_new_applicant_email_to_admins(&data.username, &mut context.pool(), context.settings())
|
||||||
&mut context.pool(),
|
.await?;
|
||||||
context.settings(),
|
}
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
login_response.verify_email_sent = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if require_registration_application {
|
let mut login_response = LoginResponse {
|
||||||
login_response.registration_created = true;
|
jwt: None,
|
||||||
}
|
registration_created: false,
|
||||||
|
verify_email_sent: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Log the user in directly if the site is not setup, or email verification and application aren't required
|
||||||
|
if !local_site.site_setup
|
||||||
|
|| (!require_registration_application && !local_site.require_email_verification)
|
||||||
|
{
|
||||||
|
login_response.jwt = Some(
|
||||||
|
Claims::jwt(
|
||||||
|
inserted_local_user.id.0,
|
||||||
|
&context.secret().jwt_secret,
|
||||||
|
&context.settings().hostname,
|
||||||
|
)?
|
||||||
|
.into(),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
if local_site.require_email_verification {
|
||||||
|
let local_user_view = LocalUserView {
|
||||||
|
local_user: inserted_local_user,
|
||||||
|
person: inserted_person,
|
||||||
|
counts: PersonAggregates::default(),
|
||||||
|
};
|
||||||
|
// we check at the beginning of this method that email is set
|
||||||
|
let email = local_user_view
|
||||||
|
.local_user
|
||||||
|
.email
|
||||||
|
.clone()
|
||||||
|
.expect("email was provided");
|
||||||
|
|
||||||
|
send_verification_email(
|
||||||
|
&local_user_view,
|
||||||
|
&email,
|
||||||
|
&mut context.pool(),
|
||||||
|
context.settings(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
login_response.verify_email_sent = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(login_response)
|
if require_registration_application {
|
||||||
|
login_response.registration_created = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(Json(login_response))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +1,36 @@
|
||||||
use crate::PerformCrud;
|
use activitypub_federation::config::Data;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Json;
|
||||||
use bcrypt::verify;
|
use bcrypt::verify;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
person::{DeleteAccount, DeleteAccountResponse},
|
person::{DeleteAccount, DeleteAccountResponse},
|
||||||
|
send_activity::{ActivityChannel, SendActivityData},
|
||||||
utils::local_user_view_from_jwt,
|
utils::local_user_view_from_jwt,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||||
|
|
||||||
#[async_trait::async_trait(?Send)]
|
#[tracing::instrument(skip(context))]
|
||||||
impl PerformCrud for DeleteAccount {
|
pub async fn delete_account(
|
||||||
type Response = DeleteAccountResponse;
|
data: Json<DeleteAccount>,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<Json<DeleteAccountResponse>, LemmyError> {
|
||||||
|
let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
|
||||||
|
|
||||||
#[tracing::instrument(skip(self, context))]
|
// Verify the password
|
||||||
async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError> {
|
let valid: bool = verify(
|
||||||
let data = self;
|
&data.password,
|
||||||
let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), context).await?;
|
&local_user_view.local_user.password_encrypted,
|
||||||
|
)
|
||||||
// Verify the password
|
.unwrap_or(false);
|
||||||
let valid: bool = verify(
|
if !valid {
|
||||||
&data.password,
|
return Err(LemmyErrorType::IncorrectLogin)?;
|
||||||
&local_user_view.local_user.password_encrypted,
|
|
||||||
)
|
|
||||||
.unwrap_or(false);
|
|
||||||
if !valid {
|
|
||||||
return Err(LemmyErrorType::IncorrectLogin)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(DeleteAccountResponse {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ActivityChannel::submit_activity(
|
||||||
|
SendActivityData::DeleteUser(local_user_view.person),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Json(DeleteAccountResponse {}))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
mod create;
|
pub mod create;
|
||||||
mod delete;
|
pub mod delete;
|
||||||
|
|
|
@ -33,7 +33,6 @@ http = { workspace = true }
|
||||||
futures = { workspace = true }
|
futures = { workspace = true }
|
||||||
itertools = { workspace = true }
|
itertools = { workspace = true }
|
||||||
uuid = { workspace = true }
|
uuid = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
|
||||||
async-trait = { workspace = true }
|
async-trait = { workspace = true }
|
||||||
anyhow = { workspace = true }
|
anyhow = { workspace = true }
|
||||||
reqwest = { workspace = true }
|
reqwest = { workspace = true }
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::{community::ApubCommunity, instance::ApubSite, person::ApubPerson},
|
objects::{community::ApubCommunity, instance::ApubSite},
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||||
objects::{group::Group, instance::Instance},
|
objects::{group::Group, instance::Instance},
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -12,19 +11,18 @@ use activitypub_federation::{
|
||||||
traits::{Actor, Object},
|
traits::{Actor, Object},
|
||||||
};
|
};
|
||||||
use chrono::NaiveDateTime;
|
use chrono::NaiveDateTime;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{community::BanFromCommunity, context::LemmyContext, person::BanPerson};
|
||||||
community::{BanFromCommunity, BanFromCommunityResponse},
|
|
||||||
context::LemmyContext,
|
|
||||||
person::{BanPerson, BanPersonResponse},
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
newtypes::CommunityId,
|
||||||
source::{community::Community, person::Person, site::Site},
|
source::{community::Community, person::Person, site::Site},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::DbPool,
|
utils::DbPool,
|
||||||
};
|
};
|
||||||
use lemmy_db_views::structs::SiteView;
|
use lemmy_db_views::structs::SiteView;
|
||||||
use lemmy_utils::{error::LemmyError, utils::time::naive_from_unix};
|
use lemmy_utils::{
|
||||||
|
error::{LemmyError, LemmyResult},
|
||||||
|
utils::time::naive_from_unix,
|
||||||
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
|
@ -132,87 +130,74 @@ async fn generate_cc(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_ban_from_site(
|
||||||
impl SendActivity for BanPerson {
|
mod_: Person,
|
||||||
type Response = BanPersonResponse;
|
banned_user: Person,
|
||||||
|
data: BanPerson,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let site = SiteOrCommunity::Site(SiteView::read_local(&mut context.pool()).await?.site.into());
|
||||||
|
let expires = data.expires.map(naive_from_unix);
|
||||||
|
|
||||||
async fn send_activity(
|
// if the action affects a local user, federate to other instances
|
||||||
request: &Self,
|
if banned_user.local {
|
||||||
_response: &Self::Response,
|
if data.ban {
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let person = Person::read(&mut context.pool(), request.person_id).await?;
|
|
||||||
let site = SiteOrCommunity::Site(SiteView::read_local(&mut context.pool()).await?.site.into());
|
|
||||||
let expires = request.expires.map(naive_from_unix);
|
|
||||||
|
|
||||||
// if the action affects a local user, federate to other instances
|
|
||||||
if person.local {
|
|
||||||
if request.ban {
|
|
||||||
BlockUser::send(
|
|
||||||
&site,
|
|
||||||
&person.into(),
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
request.remove_data.unwrap_or(false),
|
|
||||||
request.reason.clone(),
|
|
||||||
expires,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
UndoBlockUser::send(
|
|
||||||
&site,
|
|
||||||
&person.into(),
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
request.reason.clone(),
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for BanFromCommunity {
|
|
||||||
type Response = BanFromCommunityResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
_response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id)
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
let banned_person: ApubPerson = Person::read(&mut context.pool(), request.person_id)
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
let expires = request.expires.map(naive_from_unix);
|
|
||||||
|
|
||||||
if request.ban {
|
|
||||||
BlockUser::send(
|
BlockUser::send(
|
||||||
&SiteOrCommunity::Community(community),
|
&site,
|
||||||
&banned_person,
|
&banned_user.into(),
|
||||||
&local_user_view.person.clone().into(),
|
&mod_.into(),
|
||||||
request.remove_data.unwrap_or(false),
|
data.remove_data.unwrap_or(false),
|
||||||
request.reason.clone(),
|
data.reason.clone(),
|
||||||
expires,
|
expires,
|
||||||
context,
|
&context,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
} else {
|
} else {
|
||||||
UndoBlockUser::send(
|
UndoBlockUser::send(
|
||||||
&SiteOrCommunity::Community(community),
|
&site,
|
||||||
&banned_person,
|
&banned_user.into(),
|
||||||
&local_user_view.person.clone().into(),
|
&mod_.into(),
|
||||||
request.reason.clone(),
|
data.reason.clone(),
|
||||||
context,
|
&context,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn send_ban_from_community(
|
||||||
|
mod_: Person,
|
||||||
|
community_id: CommunityId,
|
||||||
|
banned_person: Person,
|
||||||
|
data: BanFromCommunity,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> LemmyResult<()> {
|
||||||
|
let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
|
||||||
|
.await?
|
||||||
|
.into();
|
||||||
|
let expires = data.expires.map(naive_from_unix);
|
||||||
|
|
||||||
|
if data.ban {
|
||||||
|
BlockUser::send(
|
||||||
|
&SiteOrCommunity::Community(community),
|
||||||
|
&banned_person.into(),
|
||||||
|
&mod_.into(),
|
||||||
|
data.remove_data.unwrap_or(false),
|
||||||
|
data.reason.clone(),
|
||||||
|
expires,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
} else {
|
||||||
|
UndoBlockUser::send(
|
||||||
|
&SiteOrCommunity::Community(community),
|
||||||
|
&banned_person.into(),
|
||||||
|
&mod_.into(),
|
||||||
|
data.reason.clone(),
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ use crate::{
|
||||||
activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
|
activities::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -22,13 +21,12 @@ use activitypub_federation::{
|
||||||
traits::{ActivityHandler, Actor},
|
traits::{ActivityHandler, Actor},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
community::{AddModToCommunity, AddModToCommunityResponse},
|
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
post::{FeaturePost, PostResponse},
|
utils::{generate_featured_url, generate_moderators_url},
|
||||||
utils::{generate_featured_url, generate_moderators_url, local_user_view_from_jwt},
|
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
impls::community::CollectionType,
|
impls::community::CollectionType,
|
||||||
|
newtypes::{CommunityId, PersonId},
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
community::{Community, CommunityModerator, CommunityModeratorForm},
|
community::{Community, CommunityModerator, CommunityModeratorForm},
|
||||||
|
@ -174,61 +172,41 @@ impl ActivityHandler for CollectionAdd {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_add_mod_to_community(
|
||||||
impl SendActivity for AddModToCommunity {
|
actor: Person,
|
||||||
type Response = AddModToCommunityResponse;
|
community_id: CommunityId,
|
||||||
|
updated_mod_id: PersonId,
|
||||||
async fn send_activity(
|
added: bool,
|
||||||
request: &Self,
|
context: Data<LemmyContext>,
|
||||||
_response: &Self::Response,
|
) -> Result<(), LemmyError> {
|
||||||
context: &Data<LemmyContext>,
|
let actor: ApubPerson = actor.into();
|
||||||
) -> Result<(), LemmyError> {
|
let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
.await?
|
||||||
let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id)
|
.into();
|
||||||
.await?
|
let updated_mod: ApubPerson = Person::read(&mut context.pool(), updated_mod_id)
|
||||||
.into();
|
.await?
|
||||||
let updated_mod: ApubPerson = Person::read(&mut context.pool(), request.person_id)
|
.into();
|
||||||
.await?
|
if added {
|
||||||
.into();
|
CollectionAdd::send_add_mod(&community, &updated_mod, &actor, &context).await
|
||||||
if request.added {
|
} else {
|
||||||
CollectionAdd::send_add_mod(
|
CollectionRemove::send_remove_mod(&community, &updated_mod, &actor, &context).await
|
||||||
&community,
|
|
||||||
&updated_mod,
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
} else {
|
|
||||||
CollectionRemove::send_remove_mod(
|
|
||||||
&community,
|
|
||||||
&updated_mod,
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_feature_post(
|
||||||
impl SendActivity for FeaturePost {
|
post: Post,
|
||||||
type Response = PostResponse;
|
actor: Person,
|
||||||
|
featured: bool,
|
||||||
async fn send_activity(
|
context: Data<LemmyContext>,
|
||||||
request: &Self,
|
) -> Result<(), LemmyError> {
|
||||||
response: &Self::Response,
|
let actor: ApubPerson = actor.into();
|
||||||
context: &Data<LemmyContext>,
|
let post: ApubPost = post.into();
|
||||||
) -> Result<(), LemmyError> {
|
let community = Community::read(&mut context.pool(), post.community_id)
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
.await?
|
||||||
let community = Community::read(&mut context.pool(), response.post_view.community.id)
|
.into();
|
||||||
.await?
|
if featured {
|
||||||
.into();
|
CollectionAdd::send_add_featured_post(&community, &post, &actor, &context).await
|
||||||
let post = response.post_view.post.clone().into();
|
} else {
|
||||||
let person = local_user_view.person.into();
|
CollectionRemove::send_remove_featured_post(&community, &post, &actor, &context).await
|
||||||
if request.featured {
|
|
||||||
CollectionAdd::send_add_featured_post(&community, &post, &person, context).await
|
|
||||||
} else {
|
|
||||||
CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,26 +9,24 @@ use crate::{
|
||||||
},
|
},
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
insert_received_activity,
|
insert_received_activity,
|
||||||
|
objects::community::ApubCommunity,
|
||||||
protocol::{
|
protocol::{
|
||||||
activities::community::lock_page::{LockPage, LockType, UndoLockPage},
|
activities::community::lock_page::{LockPage, LockType, UndoLockPage},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
fetch::object_id::ObjectId,
|
||||||
kinds::{activity::UndoType, public},
|
kinds::{activity::UndoType, public},
|
||||||
traits::ActivityHandler,
|
traits::ActivityHandler,
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
context::LemmyContext,
|
|
||||||
post::{LockPost, PostResponse},
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
community::Community,
|
community::Community,
|
||||||
|
person::Person,
|
||||||
post::{Post, PostUpdateForm},
|
post::{Post, PostUpdateForm},
|
||||||
},
|
},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
|
@ -103,59 +101,55 @@ impl ActivityHandler for UndoLockPage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_lock_post(
|
||||||
impl SendActivity for LockPost {
|
post: Post,
|
||||||
type Response = PostResponse;
|
actor: Person,
|
||||||
|
locked: bool,
|
||||||
async fn send_activity(
|
context: Data<LemmyContext>,
|
||||||
request: &Self,
|
) -> Result<(), LemmyError> {
|
||||||
response: &Self::Response,
|
let community: ApubCommunity = Community::read(&mut context.pool(), post.community_id)
|
||||||
context: &Data<LemmyContext>,
|
.await?
|
||||||
) -> Result<(), LemmyError> {
|
.into();
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
let id = generate_activity_id(
|
||||||
|
LockType::Lock,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
|
let community_id = community.actor_id.inner().clone();
|
||||||
|
let lock = LockPage {
|
||||||
|
actor: actor.actor_id.clone().into(),
|
||||||
|
to: vec![public()],
|
||||||
|
object: ObjectId::from(post.ap_id),
|
||||||
|
cc: vec![community_id.clone()],
|
||||||
|
kind: LockType::Lock,
|
||||||
|
id,
|
||||||
|
audience: Some(community_id.into()),
|
||||||
|
};
|
||||||
|
let activity = if locked {
|
||||||
|
AnnouncableActivities::LockPost(lock)
|
||||||
|
} else {
|
||||||
let id = generate_activity_id(
|
let id = generate_activity_id(
|
||||||
LockType::Lock,
|
UndoType::Undo,
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&context.settings().get_protocol_and_hostname(),
|
||||||
)?;
|
)?;
|
||||||
let community_id = response.post_view.community.actor_id.clone();
|
let undo = UndoLockPage {
|
||||||
let actor = local_user_view.person.actor_id.clone().into();
|
actor: lock.actor.clone(),
|
||||||
let lock = LockPage {
|
|
||||||
actor,
|
|
||||||
to: vec![public()],
|
to: vec![public()],
|
||||||
object: response.post_view.post.ap_id.clone().into(),
|
cc: lock.cc.clone(),
|
||||||
cc: vec![community_id.clone().into()],
|
kind: UndoType::Undo,
|
||||||
kind: LockType::Lock,
|
|
||||||
id,
|
id,
|
||||||
audience: Some(community_id.into()),
|
audience: lock.audience.clone(),
|
||||||
|
object: lock,
|
||||||
};
|
};
|
||||||
let activity = if request.locked {
|
AnnouncableActivities::UndoLockPost(undo)
|
||||||
AnnouncableActivities::LockPost(lock)
|
};
|
||||||
} else {
|
send_activity_in_community(
|
||||||
let id = generate_activity_id(
|
activity,
|
||||||
UndoType::Undo,
|
&actor.into(),
|
||||||
&context.settings().get_protocol_and_hostname(),
|
&community,
|
||||||
)?;
|
ActivitySendTargets::empty(),
|
||||||
let undo = UndoLockPage {
|
true,
|
||||||
actor: lock.actor.clone(),
|
&context,
|
||||||
to: vec![public()],
|
)
|
||||||
cc: lock.cc.clone(),
|
.await?;
|
||||||
kind: UndoType::Undo,
|
Ok(())
|
||||||
id,
|
|
||||||
audience: lock.audience.clone(),
|
|
||||||
object: lock,
|
|
||||||
};
|
|
||||||
AnnouncableActivities::UndoLockPost(undo)
|
|
||||||
};
|
|
||||||
let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
|
|
||||||
send_activity_in_community(
|
|
||||||
activity,
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
&community.into(),
|
|
||||||
ActivitySendTargets::empty(),
|
|
||||||
true,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use crate::{
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{activities::community::report::Report, InCommunity},
|
protocol::{activities::community::report::Report, InCommunity},
|
||||||
PostOrComment,
|
PostOrComment,
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -12,16 +11,13 @@ use activitypub_federation::{
|
||||||
kinds::activity::FlagType,
|
kinds::activity::FlagType,
|
||||||
traits::{ActivityHandler, Actor},
|
traits::{ActivityHandler, Actor},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{context::LemmyContext, utils::sanitize_html};
|
||||||
comment::{CommentReportResponse, CreateCommentReport},
|
|
||||||
context::LemmyContext,
|
|
||||||
post::{CreatePostReport, PostReportResponse},
|
|
||||||
utils::{local_user_view_from_jwt, sanitize_html},
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
comment_report::{CommentReport, CommentReportForm},
|
comment_report::{CommentReport, CommentReportForm},
|
||||||
|
community::Community,
|
||||||
|
person::Person,
|
||||||
post_report::{PostReport, PostReportForm},
|
post_report::{PostReport, PostReportForm},
|
||||||
},
|
},
|
||||||
traits::Reportable,
|
traits::Reportable,
|
||||||
|
@ -29,58 +25,17 @@ use lemmy_db_schema::{
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for CreatePostReport {
|
|
||||||
type Response = PostReportResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
Report::send(
|
|
||||||
ObjectId::from(response.post_report_view.post.ap_id.clone()),
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
ObjectId::from(response.post_report_view.community.actor_id.clone()),
|
|
||||||
request.reason.to_string(),
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for CreateCommentReport {
|
|
||||||
type Response = CommentReportResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
Report::send(
|
|
||||||
ObjectId::from(response.comment_report_view.comment.ap_id.clone()),
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
ObjectId::from(response.comment_report_view.community.actor_id.clone()),
|
|
||||||
request.reason.to_string(),
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Report {
|
impl Report {
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn send(
|
pub(crate) async fn send(
|
||||||
object_id: ObjectId<PostOrComment>,
|
object_id: ObjectId<PostOrComment>,
|
||||||
actor: &ApubPerson,
|
actor: Person,
|
||||||
community_id: ObjectId<ApubCommunity>,
|
community: Community,
|
||||||
reason: String,
|
reason: String,
|
||||||
context: &Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = community_id.dereference_local(context).await?;
|
let actor: ApubPerson = actor.into();
|
||||||
|
let community: ApubCommunity = community.into();
|
||||||
let kind = FlagType::Flag;
|
let kind = FlagType::Flag;
|
||||||
let id = generate_activity_id(
|
let id = generate_activity_id(
|
||||||
kind.clone(),
|
kind.clone(),
|
||||||
|
@ -97,7 +52,7 @@ impl Report {
|
||||||
};
|
};
|
||||||
// todo: this should probably filter and only send if the community is remote?
|
// todo: this should probably filter and only send if the community is remote?
|
||||||
let inbox = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
|
let inbox = ActivitySendTargets::to_inbox(community.shared_inbox_or_inbox());
|
||||||
send_lemmy_activity(context, report, actor, inbox, false).await
|
send_lemmy_activity(&context, report, &actor, inbox, false).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,72 +10,51 @@ use crate::{
|
||||||
insert_received_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::{activities::community::update::UpdateCommunity, InCommunity},
|
protocol::{activities::community::update::UpdateCommunity, InCommunity},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
kinds::{activity::UpdateType, public},
|
kinds::{activity::UpdateType, public},
|
||||||
traits::{ActivityHandler, Actor, Object},
|
traits::{ActivityHandler, Actor, Object},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
community::{CommunityResponse, EditCommunity, HideCommunity},
|
|
||||||
context::LemmyContext,
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{activity::ActivitySendTargets, community::Community},
|
source::{activity::ActivitySendTargets, community::Community, person::Person},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_update_community(
|
||||||
impl SendActivity for EditCommunity {
|
community: Community,
|
||||||
type Response = CommunityResponse;
|
actor: Person,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let community: ApubCommunity = community.into();
|
||||||
|
let actor: ApubPerson = actor.into();
|
||||||
|
let id = generate_activity_id(
|
||||||
|
UpdateType::Update,
|
||||||
|
&context.settings().get_protocol_and_hostname(),
|
||||||
|
)?;
|
||||||
|
let update = UpdateCommunity {
|
||||||
|
actor: actor.id().into(),
|
||||||
|
to: vec![public()],
|
||||||
|
object: Box::new(community.clone().into_json(&context).await?),
|
||||||
|
cc: vec![community.id()],
|
||||||
|
kind: UpdateType::Update,
|
||||||
|
id: id.clone(),
|
||||||
|
audience: Some(community.id().into()),
|
||||||
|
};
|
||||||
|
|
||||||
async fn send_activity(
|
let activity = AnnouncableActivities::UpdateCommunity(update);
|
||||||
request: &Self,
|
send_activity_in_community(
|
||||||
_response: &Self::Response,
|
activity,
|
||||||
context: &Data<LemmyContext>,
|
&actor,
|
||||||
) -> Result<(), LemmyError> {
|
&community,
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
ActivitySendTargets::empty(),
|
||||||
let community = Community::read(&mut context.pool(), request.community_id).await?;
|
true,
|
||||||
UpdateCommunity::send(community.into(), &local_user_view.person.into(), context).await
|
&context,
|
||||||
}
|
)
|
||||||
}
|
.await
|
||||||
|
|
||||||
impl UpdateCommunity {
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub async fn send(
|
|
||||||
community: ApubCommunity,
|
|
||||||
actor: &ApubPerson,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let id = generate_activity_id(
|
|
||||||
UpdateType::Update,
|
|
||||||
&context.settings().get_protocol_and_hostname(),
|
|
||||||
)?;
|
|
||||||
let update = UpdateCommunity {
|
|
||||||
actor: actor.id().into(),
|
|
||||||
to: vec![public()],
|
|
||||||
object: Box::new(community.clone().into_json(context).await?),
|
|
||||||
cc: vec![community.id()],
|
|
||||||
kind: UpdateType::Update,
|
|
||||||
id: id.clone(),
|
|
||||||
audience: Some(community.id().into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let activity = AnnouncableActivities::UpdateCommunity(update);
|
|
||||||
send_activity_in_community(
|
|
||||||
activity,
|
|
||||||
actor,
|
|
||||||
&community,
|
|
||||||
ActivitySendTargets::empty(),
|
|
||||||
true,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
@ -112,18 +91,3 @@ impl ActivityHandler for UpdateCommunity {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for HideCommunity {
|
|
||||||
type Response = CommunityResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
_response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), request.community_id).await?;
|
|
||||||
UpdateCommunity::send(community.into(), &local_user_view.person.into(), context).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -14,7 +14,6 @@ use crate::{
|
||||||
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
|
activities::{create_or_update::note::CreateOrUpdateNote, CreateOrUpdateType},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -25,7 +24,6 @@ use activitypub_federation::{
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::{
|
||||||
build_response::send_local_notifs,
|
build_response::send_local_notifs,
|
||||||
comment::{CommentResponse, EditComment},
|
|
||||||
context::LemmyContext,
|
context::LemmyContext,
|
||||||
utils::{check_post_deleted_or_removed, is_mod_or_admin},
|
utils::{check_post_deleted_or_removed, is_mod_or_admin},
|
||||||
};
|
};
|
||||||
|
@ -44,25 +42,6 @@ use lemmy_db_schema::{
|
||||||
use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
|
use lemmy_utils::{error::LemmyError, utils::mention::scrape_text_for_mentions};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for EditComment {
|
|
||||||
type Response = CommentResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
_request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
CreateOrUpdateNote::send(
|
|
||||||
response.comment_view.comment.clone(),
|
|
||||||
response.comment_view.creator.id,
|
|
||||||
CreateOrUpdateType::Update,
|
|
||||||
context.reset_request_count(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateOrUpdateNote {
|
impl CreateOrUpdateNote {
|
||||||
#[tracing::instrument(skip(comment, person_id, kind, context))]
|
#[tracing::instrument(skip(comment, person_id, kind, context))]
|
||||||
pub(crate) async fn send(
|
pub(crate) async fn send(
|
||||||
|
|
|
@ -14,7 +14,6 @@ use crate::{
|
||||||
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
activities::{create_or_update::page::CreateOrUpdatePage, CreateOrUpdateType},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -22,10 +21,7 @@ use activitypub_federation::{
|
||||||
protocol::verification::{verify_domains_match, verify_urls_match},
|
protocol::verification::{verify_domains_match, verify_urls_match},
|
||||||
traits::{ActivityHandler, Actor, Object},
|
traits::{ActivityHandler, Actor, Object},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
context::LemmyContext,
|
|
||||||
post::{EditPost, PostResponse},
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
aggregates::structs::PostAggregates,
|
aggregates::structs::PostAggregates,
|
||||||
newtypes::PersonId,
|
newtypes::PersonId,
|
||||||
|
@ -40,25 +36,6 @@ use lemmy_db_schema::{
|
||||||
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
use lemmy_utils::error::{LemmyError, LemmyErrorType};
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for EditPost {
|
|
||||||
type Response = PostResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
_request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
CreateOrUpdatePage::send(
|
|
||||||
response.post_view.post.clone(),
|
|
||||||
response.post_view.creator.id,
|
|
||||||
CreateOrUpdateType::Update,
|
|
||||||
context.reset_request_count(),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateOrUpdatePage {
|
impl CreateOrUpdatePage {
|
||||||
pub(crate) async fn new(
|
pub(crate) async fn new(
|
||||||
post: ApubPost,
|
post: ApubPost,
|
||||||
|
|
|
@ -6,7 +6,6 @@ use crate::{
|
||||||
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
create_or_update::chat_message::CreateOrUpdateChatMessage,
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -22,77 +21,33 @@ use lemmy_db_schema::{
|
||||||
source::{activity::ActivitySendTargets, person::Person, private_message::PrivateMessage},
|
source::{activity::ActivitySendTargets, person::Person, private_message::PrivateMessage},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
};
|
};
|
||||||
|
use lemmy_db_views::structs::PrivateMessageView;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_create_or_update_pm(
|
||||||
impl SendActivity for CreatePrivateMessage {
|
pm_view: PrivateMessageView,
|
||||||
type Response = PrivateMessageResponse;
|
kind: CreateOrUpdateType,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let actor: ApubPerson = pm_view.creator.into();
|
||||||
|
let recipient: ApubPerson = pm_view.recipient.into();
|
||||||
|
|
||||||
async fn send_activity(
|
let id = generate_activity_id(
|
||||||
_request: &Self,
|
kind.clone(),
|
||||||
response: &Self::Response,
|
&context.settings().get_protocol_and_hostname(),
|
||||||
context: &Data<LemmyContext>,
|
)?;
|
||||||
) -> Result<(), LemmyError> {
|
let create_or_update = CreateOrUpdateChatMessage {
|
||||||
CreateOrUpdateChatMessage::send(
|
id: id.clone(),
|
||||||
&response.private_message_view.private_message,
|
actor: actor.id().into(),
|
||||||
response.private_message_view.creator.id,
|
to: [recipient.id().into()],
|
||||||
CreateOrUpdateType::Create,
|
object: ApubPrivateMessage(pm_view.private_message.clone())
|
||||||
context,
|
.into_json(&context)
|
||||||
)
|
.await?,
|
||||||
.await
|
kind,
|
||||||
}
|
};
|
||||||
}
|
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
|
||||||
#[async_trait::async_trait]
|
send_lemmy_activity(&context, create_or_update, &actor, inbox, true).await
|
||||||
impl SendActivity for EditPrivateMessage {
|
|
||||||
type Response = PrivateMessageResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
_request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
CreateOrUpdateChatMessage::send(
|
|
||||||
&response.private_message_view.private_message,
|
|
||||||
response.private_message_view.creator.id,
|
|
||||||
CreateOrUpdateType::Update,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CreateOrUpdateChatMessage {
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
async fn send(
|
|
||||||
private_message: &PrivateMessage,
|
|
||||||
sender_id: PersonId,
|
|
||||||
kind: CreateOrUpdateType,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let recipient_id = private_message.recipient_id;
|
|
||||||
let sender: ApubPerson = Person::read(&mut context.pool(), sender_id).await?.into();
|
|
||||||
let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let id = generate_activity_id(
|
|
||||||
kind.clone(),
|
|
||||||
&context.settings().get_protocol_and_hostname(),
|
|
||||||
)?;
|
|
||||||
let create_or_update = CreateOrUpdateChatMessage {
|
|
||||||
id: id.clone(),
|
|
||||||
actor: sender.id().into(),
|
|
||||||
to: [recipient.id().into()],
|
|
||||||
object: ApubPrivateMessage(private_message.clone())
|
|
||||||
.into_json(context)
|
|
||||||
.await?,
|
|
||||||
kind,
|
|
||||||
};
|
|
||||||
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
|
|
||||||
|
|
||||||
send_lemmy_activity(context, create_or_update, &sender, inbox, true).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
|
|
|
@ -3,7 +3,6 @@ use crate::{
|
||||||
insert_received_activity,
|
insert_received_activity,
|
||||||
objects::person::ApubPerson,
|
objects::person::ApubPerson,
|
||||||
protocol::activities::deletion::delete_user::DeleteUser,
|
protocol::activities::deletion::delete_user::DeleteUser,
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -17,45 +16,38 @@ use lemmy_api_common::{
|
||||||
utils::{delete_user_account, local_user_view_from_jwt},
|
utils::{delete_user_account, local_user_view_from_jwt},
|
||||||
};
|
};
|
||||||
use lemmy_db_schema::source::activity::ActivitySendTargets;
|
use lemmy_db_schema::source::activity::ActivitySendTargets;
|
||||||
|
use lemmy_api_common::{context::LemmyContext, utils::delete_user_account};
|
||||||
|
use lemmy_db_schema::source::person::Person;
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub async fn delete_user(person: Person, context: Data<LemmyContext>) -> Result<(), LemmyError> {
|
||||||
impl SendActivity for DeleteAccount {
|
let actor: ApubPerson = person.into();
|
||||||
type Response = DeleteAccountResponse;
|
delete_user_account(
|
||||||
|
actor.id,
|
||||||
|
&mut context.pool(),
|
||||||
|
context.settings(),
|
||||||
|
context.client(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
async fn send_activity(
|
let id = generate_activity_id(
|
||||||
request: &Self,
|
DeleteType::Delete,
|
||||||
_response: &Self::Response,
|
&context.settings().get_protocol_and_hostname(),
|
||||||
context: &Data<LemmyContext>,
|
)?;
|
||||||
) -> Result<(), LemmyError> {
|
let delete = DeleteUser {
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
actor: actor.id().into(),
|
||||||
let actor: ApubPerson = local_user_view.person.into();
|
to: vec![public()],
|
||||||
delete_user_account(
|
object: actor.id().into(),
|
||||||
actor.id,
|
kind: DeleteType::Delete,
|
||||||
&mut context.pool(),
|
id: id.clone(),
|
||||||
context.settings(),
|
cc: vec![],
|
||||||
context.client(),
|
};
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
|
|
||||||
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![],
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut inboxes = ActivitySendTargets::empty();
|
let mut inboxes = ActivitySendTargets::empty();
|
||||||
inboxes.set_all_instances(true);
|
inboxes.set_all_instances(true);
|
||||||
|
|
||||||
send_lemmy_activity(context, delete, &actor, inboxes, true).await?;
|
send_lemmy_activity(&context, delete, &actor, inboxes, true).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ use crate::{
|
||||||
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||||
InCommunity,
|
InCommunity,
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -28,15 +27,9 @@ use activitypub_federation::{
|
||||||
protocol::verification::verify_domains_match,
|
protocol::verification::verify_domains_match,
|
||||||
traits::{Actor, Object},
|
traits::{Actor, Object},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
comment::{CommentResponse, DeleteComment, RemoveComment},
|
|
||||||
community::{CommunityResponse, DeleteCommunity, RemoveCommunity},
|
|
||||||
context::LemmyContext,
|
|
||||||
post::{DeletePost, PostResponse, RemovePost},
|
|
||||||
private_message::{DeletePrivateMessage, PrivateMessageResponse},
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
|
newtypes::CommunityId,
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
comment::{Comment, CommentUpdateForm},
|
comment::{Comment, CommentUpdateForm},
|
||||||
|
@ -55,170 +48,10 @@ pub mod delete;
|
||||||
pub mod delete_user;
|
pub mod delete_user;
|
||||||
pub mod undo_delete;
|
pub mod undo_delete;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for DeletePost {
|
|
||||||
type Response = PostResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
|
|
||||||
let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
|
|
||||||
send_apub_delete_in_community(
|
|
||||||
local_user_view.person,
|
|
||||||
community,
|
|
||||||
deletable,
|
|
||||||
None,
|
|
||||||
request.deleted,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for RemovePost {
|
|
||||||
type Response = PostResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
|
|
||||||
let deletable = DeletableObjects::Post(response.post_view.post.clone().into());
|
|
||||||
send_apub_delete_in_community(
|
|
||||||
local_user_view.person,
|
|
||||||
community,
|
|
||||||
deletable,
|
|
||||||
request.reason.clone().or_else(|| Some(String::new())),
|
|
||||||
request.removed,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for DeleteComment {
|
|
||||||
type Response = CommentResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let community_id = response.comment_view.community.id;
|
|
||||||
let community = Community::read(&mut context.pool(), community_id).await?;
|
|
||||||
let person = Person::read(&mut context.pool(), response.comment_view.creator.id).await?;
|
|
||||||
let deletable = DeletableObjects::Comment(response.comment_view.comment.clone().into());
|
|
||||||
send_apub_delete_in_community(person, community, deletable, None, request.deleted, context)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for RemoveComment {
|
|
||||||
type Response = CommentResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let comment = Comment::read(&mut context.pool(), request.comment_id).await?;
|
|
||||||
let community =
|
|
||||||
Community::read(&mut context.pool(), response.comment_view.community.id).await?;
|
|
||||||
let deletable = DeletableObjects::Comment(comment.into());
|
|
||||||
send_apub_delete_in_community(
|
|
||||||
local_user_view.person,
|
|
||||||
community,
|
|
||||||
deletable,
|
|
||||||
request.reason.clone().or_else(|| Some(String::new())),
|
|
||||||
request.removed,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for DeletePrivateMessage {
|
|
||||||
type Response = PrivateMessageResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
send_apub_delete_private_message(
|
|
||||||
&local_user_view.person.into(),
|
|
||||||
response.private_message_view.private_message.clone(),
|
|
||||||
request.deleted,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for DeleteCommunity {
|
|
||||||
type Response = CommunityResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
_response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), request.community_id).await?;
|
|
||||||
let deletable = DeletableObjects::Community(community.clone().into());
|
|
||||||
send_apub_delete_in_community(
|
|
||||||
local_user_view.person,
|
|
||||||
community,
|
|
||||||
deletable,
|
|
||||||
None,
|
|
||||||
request.deleted,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for RemoveCommunity {
|
|
||||||
type Response = CommunityResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
_response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), request.community_id).await?;
|
|
||||||
let deletable = DeletableObjects::Community(community.clone().into());
|
|
||||||
send_apub_delete_in_community(
|
|
||||||
local_user_view.person,
|
|
||||||
community,
|
|
||||||
deletable,
|
|
||||||
request.reason.clone().or_else(|| Some(String::new())),
|
|
||||||
request.removed,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
||||||
/// action was done by a normal user.
|
/// action was done by a normal user.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn send_apub_delete_in_community(
|
pub(crate) async fn send_apub_delete_in_community(
|
||||||
actor: Person,
|
actor: Person,
|
||||||
community: Community,
|
community: Community,
|
||||||
object: DeletableObjects,
|
object: DeletableObjects,
|
||||||
|
@ -246,12 +79,44 @@ async fn send_apub_delete_in_community(
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parameter `reason` being set indicates that this is a removal by a mod. If its unset, this
|
||||||
|
/// action was done by a normal user.
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
async fn send_apub_delete_private_message(
|
pub(crate) async fn send_apub_delete_in_community_new(
|
||||||
|
actor: Person,
|
||||||
|
community_id: CommunityId,
|
||||||
|
object: DeletableObjects,
|
||||||
|
reason: Option<String>,
|
||||||
|
deleted: bool,
|
||||||
|
context: Data<LemmyContext>,
|
||||||
|
) -> Result<(), LemmyError> {
|
||||||
|
let community = Community::read(&mut context.pool(), community_id).await?;
|
||||||
|
let actor = ApubPerson::from(actor);
|
||||||
|
let is_mod_action = reason.is_some();
|
||||||
|
let activity = if deleted {
|
||||||
|
let delete = Delete::new(&actor, object, public(), Some(&community), reason, &context)?;
|
||||||
|
AnnouncableActivities::Delete(delete)
|
||||||
|
} else {
|
||||||
|
let undo = UndoDelete::new(&actor, object, public(), Some(&community), reason, &context)?;
|
||||||
|
AnnouncableActivities::UndoDelete(undo)
|
||||||
|
};
|
||||||
|
send_activity_in_community(
|
||||||
|
activity,
|
||||||
|
&actor,
|
||||||
|
&community.into(),
|
||||||
|
vec![],
|
||||||
|
is_mod_action,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument(skip_all)]
|
||||||
|
pub(crate) async fn send_apub_delete_private_message(
|
||||||
actor: &ApubPerson,
|
actor: &ApubPerson,
|
||||||
pm: PrivateMessage,
|
pm: PrivateMessage,
|
||||||
deleted: bool,
|
deleted: bool,
|
||||||
context: &Data<LemmyContext>,
|
context: Data<LemmyContext>,
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let recipient_id = pm.recipient_id;
|
let recipient_id = pm.recipient_id;
|
||||||
let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
|
let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
|
||||||
|
@ -261,11 +126,11 @@ async fn send_apub_delete_private_message(
|
||||||
let deletable = DeletableObjects::PrivateMessage(pm.into());
|
let deletable = DeletableObjects::PrivateMessage(pm.into());
|
||||||
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
|
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
|
||||||
if deleted {
|
if deleted {
|
||||||
let delete: Delete = Delete::new(actor, deletable, recipient.id(), None, None, context)?;
|
let delete: Delete = Delete::new(actor, deletable, recipient.id(), None, None, &context)?;
|
||||||
send_lemmy_activity(context, delete, actor, inbox, true).await?;
|
send_lemmy_activity(&context, delete, actor, inbox, true).await?;
|
||||||
} else {
|
} else {
|
||||||
let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, context)?;
|
let undo = UndoDelete::new(actor, deletable, recipient.id(), None, None, &context)?;
|
||||||
send_lemmy_activity(context, undo, actor, inbox, true).await?;
|
send_lemmy_activity(&context, undo, actor, inbox, true).await?;
|
||||||
};
|
};
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,7 @@ use crate::{
|
||||||
fetcher::user_or_community::UserOrCommunity,
|
fetcher::user_or_community::UserOrCommunity,
|
||||||
insert_received_activity,
|
insert_received_activity,
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{
|
protocol::activities::following::{accept::AcceptFollow, follow::Follow},
|
||||||
accept::AcceptFollow,
|
|
||||||
follow::Follow,
|
|
||||||
undo_follow::UndoFollow,
|
|
||||||
},
|
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{
|
use activitypub_federation::{
|
||||||
config::Data,
|
config::Data,
|
||||||
|
@ -21,18 +16,14 @@ use activitypub_federation::{
|
||||||
protocol::verification::verify_urls_match,
|
protocol::verification::verify_urls_match,
|
||||||
traits::{ActivityHandler, Actor},
|
traits::{ActivityHandler, Actor},
|
||||||
};
|
};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
community::{BlockCommunity, BlockCommunityResponse},
|
|
||||||
context::LemmyContext,
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
community::{Community, CommunityFollower, CommunityFollowerForm},
|
community::{Community, CommunityFollower, CommunityFollowerForm},
|
||||||
person::{PersonFollower, PersonFollowerForm},
|
person::{PersonFollower, PersonFollowerForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Followable},
|
traits::Followable,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
|
@ -130,18 +121,3 @@ impl ActivityHandler for Follow {
|
||||||
AcceptFollow::send(self, context).await
|
AcceptFollow::send(self, context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for BlockCommunity {
|
|
||||||
type Response = BlockCommunityResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
_response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
|
||||||
let community = Community::read(&mut context.pool(), request.community_id).await?;
|
|
||||||
UndoFollow::send(&local_user_view.person.into(), &community.into(), context).await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,41 +1,27 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
objects::community::ApubCommunity,
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
protocol::activities::following::{follow::Follow, undo_follow::UndoFollow},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::config::Data;
|
use activitypub_federation::config::Data;
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
community::{CommunityResponse, FollowCommunity},
|
use lemmy_db_schema::source::{community::Community, person::Person};
|
||||||
context::LemmyContext,
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
pub mod accept;
|
pub mod accept;
|
||||||
pub mod follow;
|
pub mod follow;
|
||||||
pub mod undo_follow;
|
pub mod undo_follow;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub async fn send_follow_community(
|
||||||
impl SendActivity for FollowCommunity {
|
community: Community,
|
||||||
type Response = CommunityResponse;
|
person: Person,
|
||||||
|
follow: bool,
|
||||||
async fn send_activity(
|
context: &Data<LemmyContext>,
|
||||||
request: &Self,
|
) -> Result<(), LemmyError> {
|
||||||
_response: &Self::Response,
|
let community: ApubCommunity = community.into();
|
||||||
context: &Data<LemmyContext>,
|
let actor: ApubPerson = person.into();
|
||||||
) -> Result<(), LemmyError> {
|
if follow {
|
||||||
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?;
|
Follow::send(&actor, &community, context).await
|
||||||
let person = local_user_view.person.clone().into();
|
} else {
|
||||||
let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id)
|
UndoFollow::send(&actor, &community, context).await
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
if community.local {
|
|
||||||
Ok(())
|
|
||||||
} else if request.follow {
|
|
||||||
Follow::send(&person, &community, context).await
|
|
||||||
} else {
|
|
||||||
UndoFollow::send(&person, &community, context).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,25 @@
|
||||||
|
use self::following::send_follow_community;
|
||||||
use crate::{
|
use crate::{
|
||||||
|
activities::{
|
||||||
|
block::{send_ban_from_community, send_ban_from_site},
|
||||||
|
community::{
|
||||||
|
collection_add::{send_add_mod_to_community, send_feature_post},
|
||||||
|
lock_page::send_lock_post,
|
||||||
|
update::send_update_community,
|
||||||
|
},
|
||||||
|
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,
|
||||||
|
DeletableObjects,
|
||||||
|
},
|
||||||
|
voting::send_like_activity,
|
||||||
|
},
|
||||||
objects::{community::ApubCommunity, person::ApubPerson},
|
objects::{community::ApubCommunity, person::ApubPerson},
|
||||||
protocol::activities::{
|
protocol::activities::{
|
||||||
|
community::report::Report,
|
||||||
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
|
create_or_update::{note::CreateOrUpdateNote, page::CreateOrUpdatePage},
|
||||||
CreateOrUpdateType,
|
CreateOrUpdateType,
|
||||||
},
|
},
|
||||||
|
@ -208,15 +227,102 @@ pub async fn match_outgoing_activities(
|
||||||
) -> LemmyResult<()> {
|
) -> LemmyResult<()> {
|
||||||
let context = context.reset_request_count();
|
let context = context.reset_request_count();
|
||||||
let fed_task = async {
|
let fed_task = async {
|
||||||
|
use SendActivityData::*;
|
||||||
match data {
|
match data {
|
||||||
SendActivityData::CreatePost(post) => {
|
CreatePost(post) => {
|
||||||
let creator_id = post.creator_id;
|
let creator_id = post.creator_id;
|
||||||
CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
|
CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Create, context).await
|
||||||
}
|
}
|
||||||
SendActivityData::CreateComment(comment) => {
|
UpdatePost(post) => {
|
||||||
|
let creator_id = post.creator_id;
|
||||||
|
CreateOrUpdatePage::send(post, creator_id, CreateOrUpdateType::Update, context).await
|
||||||
|
}
|
||||||
|
DeletePost(post, person, data) => {
|
||||||
|
send_apub_delete_in_community_new(
|
||||||
|
person,
|
||||||
|
post.community_id,
|
||||||
|
DeletableObjects::Post(post.into()),
|
||||||
|
None,
|
||||||
|
data.deleted,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
RemovePost(post, person, data) => {
|
||||||
|
send_apub_delete_in_community_new(
|
||||||
|
person,
|
||||||
|
post.community_id,
|
||||||
|
DeletableObjects::Post(post.into()),
|
||||||
|
data.reason.or_else(|| Some(String::new())),
|
||||||
|
data.removed,
|
||||||
|
context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
LockPost(post, actor, locked) => send_lock_post(post, actor, locked, context).await,
|
||||||
|
FeaturePost(post, actor, featured) => send_feature_post(post, actor, featured, context).await,
|
||||||
|
CreateComment(comment) => {
|
||||||
let creator_id = comment.creator_id;
|
let creator_id = comment.creator_id;
|
||||||
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
|
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Create, context).await
|
||||||
}
|
}
|
||||||
|
UpdateComment(comment) => {
|
||||||
|
let creator_id = comment.creator_id;
|
||||||
|
CreateOrUpdateNote::send(comment, creator_id, CreateOrUpdateType::Update, context).await
|
||||||
|
}
|
||||||
|
DeleteComment(comment, actor, community) => {
|
||||||
|
let is_deleted = comment.deleted;
|
||||||
|
let deletable = DeletableObjects::Comment(comment.into());
|
||||||
|
send_apub_delete_in_community(actor, community, deletable, None, is_deleted, &context).await
|
||||||
|
}
|
||||||
|
RemoveComment(comment, actor, community, reason) => {
|
||||||
|
let is_removed = comment.removed;
|
||||||
|
let deletable = DeletableObjects::Comment(comment.into());
|
||||||
|
send_apub_delete_in_community(actor, community, deletable, reason, is_removed, &context)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
LikePostOrComment(object_id, person, community, score) => {
|
||||||
|
send_like_activity(object_id, person, community, score, context).await
|
||||||
|
}
|
||||||
|
FollowCommunity(community, person, follow) => {
|
||||||
|
send_follow_community(community, person, follow, &context).await
|
||||||
|
}
|
||||||
|
UpdateCommunity(actor, community) => send_update_community(community, actor, context).await,
|
||||||
|
DeleteCommunity(actor, community, removed) => {
|
||||||
|
let deletable = DeletableObjects::Community(community.clone().into());
|
||||||
|
send_apub_delete_in_community(actor, community, deletable, None, removed, &context).await
|
||||||
|
}
|
||||||
|
RemoveCommunity(actor, community, reason, removed) => {
|
||||||
|
let deletable = DeletableObjects::Community(community.clone().into());
|
||||||
|
send_apub_delete_in_community(
|
||||||
|
actor,
|
||||||
|
community,
|
||||||
|
deletable,
|
||||||
|
reason.clone().or_else(|| Some(String::new())),
|
||||||
|
removed,
|
||||||
|
&context,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
AddModToCommunity(actor, community_id, updated_mod_id, added) => {
|
||||||
|
send_add_mod_to_community(actor, community_id, updated_mod_id, added, context).await
|
||||||
|
}
|
||||||
|
BanFromCommunity(mod_, community_id, target, data) => {
|
||||||
|
send_ban_from_community(mod_, community_id, target, data, context).await
|
||||||
|
}
|
||||||
|
BanFromSite(mod_, target, data) => send_ban_from_site(mod_, target, data, context).await,
|
||||||
|
CreatePrivateMessage(pm) => {
|
||||||
|
send_create_or_update_pm(pm, CreateOrUpdateType::Create, context).await
|
||||||
|
}
|
||||||
|
UpdatePrivateMessage(pm) => {
|
||||||
|
send_create_or_update_pm(pm, CreateOrUpdateType::Update, context).await
|
||||||
|
}
|
||||||
|
DeletePrivateMessage(person, pm, deleted) => {
|
||||||
|
send_apub_delete_private_message(&person.into(), pm, deleted, context).await
|
||||||
|
}
|
||||||
|
DeleteUser(person) => delete_user(person, context).await,
|
||||||
|
CreateReport(url, actor, community, reason) => {
|
||||||
|
Report::send(ObjectId::from(url), actor, community, reason, context).await
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if *SYNCHRONOUS_FEDERATION {
|
if *SYNCHRONOUS_FEDERATION {
|
||||||
|
|
|
@ -2,23 +2,16 @@ use crate::{
|
||||||
activities::community::send_activity_in_community,
|
activities::community::send_activity_in_community,
|
||||||
activity_lists::AnnouncableActivities,
|
activity_lists::AnnouncableActivities,
|
||||||
fetcher::post_or_comment::PostOrComment,
|
fetcher::post_or_comment::PostOrComment,
|
||||||
objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
|
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||||
protocol::activities::voting::{
|
protocol::activities::voting::{
|
||||||
undo_vote::UndoVote,
|
undo_vote::UndoVote,
|
||||||
vote::{Vote, VoteType},
|
vote::{Vote, VoteType},
|
||||||
},
|
},
|
||||||
SendActivity,
|
|
||||||
};
|
};
|
||||||
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
|
||||||
use lemmy_api_common::{
|
use lemmy_api_common::context::LemmyContext;
|
||||||
comment::{CommentResponse, CreateCommentLike},
|
|
||||||
context::LemmyContext,
|
|
||||||
post::{CreatePostLike, PostResponse},
|
|
||||||
sensitive::Sensitive,
|
|
||||||
utils::local_user_view_from_jwt,
|
|
||||||
};
|
|
||||||
use lemmy_db_schema::{
|
use lemmy_db_schema::{
|
||||||
newtypes::CommunityId,
|
newtypes::DbUrl,
|
||||||
source::{
|
source::{
|
||||||
activity::ActivitySendTargets,
|
activity::ActivitySendTargets,
|
||||||
comment::{CommentLike, CommentLikeForm},
|
comment::{CommentLike, CommentLikeForm},
|
||||||
|
@ -26,84 +19,36 @@ use lemmy_db_schema::{
|
||||||
person::Person,
|
person::Person,
|
||||||
post::{PostLike, PostLikeForm},
|
post::{PostLike, PostLikeForm},
|
||||||
},
|
},
|
||||||
traits::{Crud, Likeable},
|
traits::Likeable,
|
||||||
};
|
};
|
||||||
use lemmy_utils::error::LemmyError;
|
use lemmy_utils::error::LemmyError;
|
||||||
|
|
||||||
pub mod undo_vote;
|
pub mod undo_vote;
|
||||||
pub mod vote;
|
pub mod vote;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
pub(crate) async fn send_like_activity(
|
||||||
impl SendActivity for CreatePostLike {
|
object_id: DbUrl,
|
||||||
type Response = PostResponse;
|
actor: Person,
|
||||||
|
community: Community,
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
|
|
||||||
let community_id = response.post_view.community.id;
|
|
||||||
send_activity(
|
|
||||||
object_id,
|
|
||||||
community_id,
|
|
||||||
request.score,
|
|
||||||
&request.auth,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl SendActivity for CreateCommentLike {
|
|
||||||
type Response = CommentResponse;
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
request: &Self,
|
|
||||||
response: &Self::Response,
|
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
|
||||||
let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
|
|
||||||
let community_id = response.comment_view.community.id;
|
|
||||||
send_activity(
|
|
||||||
object_id,
|
|
||||||
community_id,
|
|
||||||
request.score,
|
|
||||||
&request.auth,
|
|
||||||
context,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn send_activity(
|
|
||||||
object_id: ObjectId<PostOrComment>,
|
|
||||||
community_id: CommunityId,
|
|
||||||
score: i16,
|
score: i16,
|
||||||
jwt: &Sensitive<String>,
|
context: Data<LemmyContext>,
|
||||||
context: &Data<LemmyContext>,
|
|
||||||
) -> Result<(), LemmyError> {
|
) -> Result<(), LemmyError> {
|
||||||
let community = Community::read(&mut context.pool(), community_id)
|
let object_id: ObjectId<PostOrComment> = object_id.try_into()?;
|
||||||
.await?
|
let actor: ApubPerson = actor.into();
|
||||||
.into();
|
let community: ApubCommunity = community.into();
|
||||||
let local_user_view = local_user_view_from_jwt(jwt, context).await?;
|
|
||||||
let actor = Person::read(&mut context.pool(), local_user_view.person.id)
|
|
||||||
.await?
|
|
||||||
.into();
|
|
||||||
|
|
||||||
let empty = ActivitySendTargets::empty();
|
let empty = ActivitySendTargets::empty();
|
||||||
// score of 1 means upvote, -1 downvote, 0 undo a previous vote
|
// score of 1 means upvote, -1 downvote, 0 undo a previous vote
|
||||||
if score != 0 {
|
if score != 0 {
|
||||||
let vote = Vote::new(object_id, &actor, &community, score.try_into()?, context)?;
|
let vote = Vote::new(object_id, &actor, &community, score.try_into()?, &context)?;
|
||||||
let activity = AnnouncableActivities::Vote(vote);
|
let activity = AnnouncableActivities::Vote(vote);
|
||||||
send_activity_in_community(activity, &actor, &community, empty, false, context).await
|
send_activity_in_community(activity, &actor, &community, empty, false, &context).await
|
||||||
} else {
|
} else {
|
||||||
// Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
|
// Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
|
||||||
let vote = Vote::new(object_id, &actor, &community, VoteType::Like, context)?;
|
let vote = Vote::new(object_id, &actor, &community, VoteType::Like, &context)?;
|
||||||
let undo_vote = UndoVote::new(vote, &actor, &community, context)?;
|
let undo_vote = UndoVote::new(vote, &actor, &community, &context)?;
|
||||||
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
||||||
send_activity_in_community(activity, &actor, &community, empty, false, context).await
|
send_activity_in_community(activity, &actor, &community, empty, false, &context).await
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,7 +22,6 @@ full = [
|
||||||
"bcrypt",
|
"bcrypt",
|
||||||
"lemmy_utils",
|
"lemmy_utils",
|
||||||
"activitypub_federation",
|
"activitypub_federation",
|
||||||
"sha2",
|
|
||||||
"regex",
|
"regex",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
@ -60,7 +59,6 @@ diesel-async = { workspace = true, features = [
|
||||||
"postgres",
|
"postgres",
|
||||||
"deadpool",
|
"deadpool",
|
||||||
], optional = true }
|
], optional = true }
|
||||||
sha2 = { workspace = true, optional = true }
|
|
||||||
regex = { workspace = true, optional = true }
|
regex = { workspace = true, optional = true }
|
||||||
once_cell = { workspace = true, optional = true }
|
once_cell = { workspace = true, optional = true }
|
||||||
diesel_ltree = { workspace = true, optional = true }
|
diesel_ltree = { workspace = true, optional = true }
|
||||||
|
|
|
@ -156,15 +156,6 @@ impl Crud for Comment {
|
||||||
type InsertForm = CommentInsertForm;
|
type InsertForm = CommentInsertForm;
|
||||||
type UpdateForm = CommentUpdateForm;
|
type UpdateForm = CommentUpdateForm;
|
||||||
type IdType = CommentId;
|
type IdType = CommentId;
|
||||||
async fn read(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
comment.find(comment_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, comment_id: CommentId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(comment.find(comment_id)).execute(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This is unimplemented, use [[Comment::create]]
|
/// This is unimplemented, use [[Comment::create]]
|
||||||
async fn create(_pool: &mut DbPool<'_>, _comment_form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(_pool: &mut DbPool<'_>, _comment_form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
|
|
|
@ -13,13 +13,6 @@ impl Crud for CommentReply {
|
||||||
type InsertForm = CommentReplyInsertForm;
|
type InsertForm = CommentReplyInsertForm;
|
||||||
type UpdateForm = CommentReplyUpdateForm;
|
type UpdateForm = CommentReplyUpdateForm;
|
||||||
type IdType = CommentReplyId;
|
type IdType = CommentReplyId;
|
||||||
async fn read(pool: &mut DbPool<'_>, comment_reply_id: CommentReplyId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
comment_reply
|
|
||||||
.find(comment_reply_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(
|
async fn create(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
|
|
|
@ -27,20 +27,6 @@ impl Crud for Community {
|
||||||
type InsertForm = CommunityInsertForm;
|
type InsertForm = CommunityInsertForm;
|
||||||
type UpdateForm = CommunityUpdateForm;
|
type UpdateForm = CommunityUpdateForm;
|
||||||
type IdType = CommunityId;
|
type IdType = CommunityId;
|
||||||
async fn read(pool: &mut DbPool<'_>, community_id: CommunityId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
community::table
|
|
||||||
.find(community_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, community_id: CommunityId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(community::table.find(community_id))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
let is_new_community = match &form.actor_id {
|
let is_new_community = match &form.actor_id {
|
||||||
|
|
|
@ -69,16 +69,7 @@ impl Crud for LocalUser {
|
||||||
type InsertForm = LocalUserInsertForm;
|
type InsertForm = LocalUserInsertForm;
|
||||||
type UpdateForm = LocalUserUpdateForm;
|
type UpdateForm = LocalUserUpdateForm;
|
||||||
type IdType = LocalUserId;
|
type IdType = LocalUserId;
|
||||||
async fn read(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
local_user.find(local_user_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, local_user_id: LocalUserId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(local_user.find(local_user_id))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let mut form_with_encrypted_password = form.clone();
|
let mut form_with_encrypted_password = form.clone();
|
||||||
|
|
|
@ -42,11 +42,6 @@ impl Crud for ModRemovePost {
|
||||||
type InsertForm = ModRemovePostForm;
|
type InsertForm = ModRemovePostForm;
|
||||||
type UpdateForm = ModRemovePostForm;
|
type UpdateForm = ModRemovePostForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_remove_post::dsl::mod_remove_post;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_remove_post.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModRemovePostForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModRemovePostForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_remove_post::dsl::mod_remove_post;
|
use crate::schema::mod_remove_post::dsl::mod_remove_post;
|
||||||
|
@ -76,11 +71,6 @@ impl Crud for ModLockPost {
|
||||||
type InsertForm = ModLockPostForm;
|
type InsertForm = ModLockPostForm;
|
||||||
type UpdateForm = ModLockPostForm;
|
type UpdateForm = ModLockPostForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_lock_post::dsl::mod_lock_post;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_lock_post.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModLockPostForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModLockPostForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_lock_post::dsl::mod_lock_post;
|
use crate::schema::mod_lock_post::dsl::mod_lock_post;
|
||||||
|
@ -110,11 +100,6 @@ impl Crud for ModFeaturePost {
|
||||||
type InsertForm = ModFeaturePostForm;
|
type InsertForm = ModFeaturePostForm;
|
||||||
type UpdateForm = ModFeaturePostForm;
|
type UpdateForm = ModFeaturePostForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_feature_post::dsl::mod_feature_post;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_feature_post.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModFeaturePostForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModFeaturePostForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_feature_post::dsl::mod_feature_post;
|
use crate::schema::mod_feature_post::dsl::mod_feature_post;
|
||||||
|
@ -144,11 +129,6 @@ impl Crud for ModRemoveComment {
|
||||||
type InsertForm = ModRemoveCommentForm;
|
type InsertForm = ModRemoveCommentForm;
|
||||||
type UpdateForm = ModRemoveCommentForm;
|
type UpdateForm = ModRemoveCommentForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_remove_comment::dsl::mod_remove_comment;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_remove_comment.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommentForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommentForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_remove_comment::dsl::mod_remove_comment;
|
use crate::schema::mod_remove_comment::dsl::mod_remove_comment;
|
||||||
|
@ -178,11 +158,6 @@ impl Crud for ModRemoveCommunity {
|
||||||
type InsertForm = ModRemoveCommunityForm;
|
type InsertForm = ModRemoveCommunityForm;
|
||||||
type UpdateForm = ModRemoveCommunityForm;
|
type UpdateForm = ModRemoveCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_remove_community::dsl::mod_remove_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_remove_community.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommunityForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModRemoveCommunityForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_remove_community::dsl::mod_remove_community;
|
use crate::schema::mod_remove_community::dsl::mod_remove_community;
|
||||||
|
@ -212,14 +187,6 @@ impl Crud for ModBanFromCommunity {
|
||||||
type InsertForm = ModBanFromCommunityForm;
|
type InsertForm = ModBanFromCommunityForm;
|
||||||
type UpdateForm = ModBanFromCommunityForm;
|
type UpdateForm = ModBanFromCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_ban_from_community::dsl::mod_ban_from_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_ban_from_community
|
|
||||||
.find(from_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModBanFromCommunityForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModBanFromCommunityForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_ban_from_community::dsl::mod_ban_from_community;
|
use crate::schema::mod_ban_from_community::dsl::mod_ban_from_community;
|
||||||
|
@ -249,11 +216,6 @@ impl Crud for ModBan {
|
||||||
type InsertForm = ModBanForm;
|
type InsertForm = ModBanForm;
|
||||||
type UpdateForm = ModBanForm;
|
type UpdateForm = ModBanForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_ban::dsl::mod_ban;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_ban.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModBanForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModBanForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_ban::dsl::mod_ban;
|
use crate::schema::mod_ban::dsl::mod_ban;
|
||||||
|
@ -280,12 +242,6 @@ impl Crud for ModHideCommunity {
|
||||||
type UpdateForm = ModHideCommunityForm;
|
type UpdateForm = ModHideCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
|
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_hide_community::dsl::mod_hide_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_hide_community.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModHideCommunityForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModHideCommunityForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_hide_community::dsl::mod_hide_community;
|
use crate::schema::mod_hide_community::dsl::mod_hide_community;
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
@ -314,11 +270,6 @@ impl Crud for ModAddCommunity {
|
||||||
type InsertForm = ModAddCommunityForm;
|
type InsertForm = ModAddCommunityForm;
|
||||||
type UpdateForm = ModAddCommunityForm;
|
type UpdateForm = ModAddCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_add_community::dsl::mod_add_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_add_community.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModAddCommunityForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModAddCommunityForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_add_community::dsl::mod_add_community;
|
use crate::schema::mod_add_community::dsl::mod_add_community;
|
||||||
|
@ -348,14 +299,6 @@ impl Crud for ModTransferCommunity {
|
||||||
type InsertForm = ModTransferCommunityForm;
|
type InsertForm = ModTransferCommunityForm;
|
||||||
type UpdateForm = ModTransferCommunityForm;
|
type UpdateForm = ModTransferCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_transfer_community::dsl::mod_transfer_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_transfer_community
|
|
||||||
.find(from_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModTransferCommunityForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModTransferCommunityForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_transfer_community::dsl::mod_transfer_community;
|
use crate::schema::mod_transfer_community::dsl::mod_transfer_community;
|
||||||
|
@ -385,11 +328,6 @@ impl Crud for ModAdd {
|
||||||
type InsertForm = ModAddForm;
|
type InsertForm = ModAddForm;
|
||||||
type UpdateForm = ModAddForm;
|
type UpdateForm = ModAddForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::mod_add::dsl::mod_add;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
mod_add.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &ModAddForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &ModAddForm) -> Result<Self, Error> {
|
||||||
use crate::schema::mod_add::dsl::mod_add;
|
use crate::schema::mod_add::dsl::mod_add;
|
||||||
|
@ -415,11 +353,6 @@ impl Crud for AdminPurgePerson {
|
||||||
type InsertForm = AdminPurgePersonForm;
|
type InsertForm = AdminPurgePersonForm;
|
||||||
type UpdateForm = AdminPurgePersonForm;
|
type UpdateForm = AdminPurgePersonForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::admin_purge_person::dsl::admin_purge_person;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
admin_purge_person.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
use crate::schema::admin_purge_person::dsl::admin_purge_person;
|
use crate::schema::admin_purge_person::dsl::admin_purge_person;
|
||||||
|
@ -449,14 +382,6 @@ impl Crud for AdminPurgeCommunity {
|
||||||
type InsertForm = AdminPurgeCommunityForm;
|
type InsertForm = AdminPurgeCommunityForm;
|
||||||
type UpdateForm = AdminPurgeCommunityForm;
|
type UpdateForm = AdminPurgeCommunityForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::admin_purge_community::dsl::admin_purge_community;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
admin_purge_community
|
|
||||||
.find(from_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
use crate::schema::admin_purge_community::dsl::admin_purge_community;
|
use crate::schema::admin_purge_community::dsl::admin_purge_community;
|
||||||
|
@ -486,11 +411,6 @@ impl Crud for AdminPurgePost {
|
||||||
type InsertForm = AdminPurgePostForm;
|
type InsertForm = AdminPurgePostForm;
|
||||||
type UpdateForm = AdminPurgePostForm;
|
type UpdateForm = AdminPurgePostForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::admin_purge_post::dsl::admin_purge_post;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
admin_purge_post.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
use crate::schema::admin_purge_post::dsl::admin_purge_post;
|
use crate::schema::admin_purge_post::dsl::admin_purge_post;
|
||||||
|
@ -520,11 +440,6 @@ impl Crud for AdminPurgeComment {
|
||||||
type InsertForm = AdminPurgeCommentForm;
|
type InsertForm = AdminPurgeCommentForm;
|
||||||
type UpdateForm = AdminPurgeCommentForm;
|
type UpdateForm = AdminPurgeCommentForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, from_id: i32) -> Result<Self, Error> {
|
|
||||||
use crate::schema::admin_purge_comment::dsl::admin_purge_comment;
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
admin_purge_comment.find(from_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
use crate::schema::admin_purge_comment::dsl::admin_purge_comment;
|
use crate::schema::admin_purge_comment::dsl::admin_purge_comment;
|
||||||
|
|
|
@ -1,11 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::LocalUserId,
|
newtypes::LocalUserId,
|
||||||
schema::password_reset_request::dsl::{
|
schema::password_reset_request::dsl::{local_user_id, password_reset_request, published, token},
|
||||||
local_user_id,
|
|
||||||
password_reset_request,
|
|
||||||
published,
|
|
||||||
token_encrypted,
|
|
||||||
},
|
|
||||||
source::password_reset_request::{PasswordResetRequest, PasswordResetRequestForm},
|
source::password_reset_request::{PasswordResetRequest, PasswordResetRequestForm},
|
||||||
traits::Crud,
|
traits::Crud,
|
||||||
utils::{get_conn, DbPool},
|
utils::{get_conn, DbPool},
|
||||||
|
@ -17,20 +12,13 @@ use diesel::{
|
||||||
QueryDsl,
|
QueryDsl,
|
||||||
};
|
};
|
||||||
use diesel_async::RunQueryDsl;
|
use diesel_async::RunQueryDsl;
|
||||||
use sha2::{Digest, Sha256};
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl Crud for PasswordResetRequest {
|
impl Crud for PasswordResetRequest {
|
||||||
type InsertForm = PasswordResetRequestForm;
|
type InsertForm = PasswordResetRequestForm;
|
||||||
type UpdateForm = PasswordResetRequestForm;
|
type UpdateForm = PasswordResetRequestForm;
|
||||||
type IdType = i32;
|
type IdType = i32;
|
||||||
async fn read(pool: &mut DbPool<'_>, password_reset_request_id: i32) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
password_reset_request
|
|
||||||
.find(password_reset_request_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &PasswordResetRequestForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &PasswordResetRequestForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(password_reset_request)
|
insert_into(password_reset_request)
|
||||||
|
@ -55,29 +43,22 @@ impl PasswordResetRequest {
|
||||||
pub async fn create_token(
|
pub async fn create_token(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
from_local_user_id: LocalUserId,
|
from_local_user_id: LocalUserId,
|
||||||
token: &str,
|
token_: String,
|
||||||
) -> Result<PasswordResetRequest, Error> {
|
) -> Result<PasswordResetRequest, Error> {
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(token);
|
|
||||||
let token_hash: String = bytes_to_hex(hasher.finalize().to_vec());
|
|
||||||
|
|
||||||
let form = PasswordResetRequestForm {
|
let form = PasswordResetRequestForm {
|
||||||
local_user_id: from_local_user_id,
|
local_user_id: from_local_user_id,
|
||||||
token_encrypted: token_hash,
|
token: token_,
|
||||||
};
|
};
|
||||||
|
|
||||||
Self::create(pool, &form).await
|
Self::create(pool, &form).await
|
||||||
}
|
}
|
||||||
pub async fn read_from_token(
|
pub async fn read_from_token(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
token: &str,
|
token_: &str,
|
||||||
) -> Result<PasswordResetRequest, Error> {
|
) -> Result<PasswordResetRequest, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
let mut hasher = Sha256::new();
|
|
||||||
hasher.update(token);
|
|
||||||
let token_hash: String = bytes_to_hex(hasher.finalize().to_vec());
|
|
||||||
password_reset_request
|
password_reset_request
|
||||||
.filter(token_encrypted.eq(token_hash))
|
.filter(token.eq(token_))
|
||||||
.filter(published.gt(now - 1.days()))
|
.filter(published.gt(now - 1.days()))
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
|
@ -97,14 +78,6 @@ impl PasswordResetRequest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bytes_to_hex(bytes: Vec<u8>) -> String {
|
|
||||||
let mut str = String::new();
|
|
||||||
for byte in bytes {
|
|
||||||
str = format!("{str}{byte:02x}");
|
|
||||||
}
|
|
||||||
str
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
#![allow(clippy::unwrap_used)]
|
#![allow(clippy::unwrap_used)]
|
||||||
|
@ -148,17 +121,16 @@ mod tests {
|
||||||
let inserted_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
|
let inserted_local_user = LocalUser::create(pool, &new_local_user).await.unwrap();
|
||||||
|
|
||||||
let token = "nope";
|
let token = "nope";
|
||||||
let token_encrypted_ = "ca3704aa0b06f5954c79ee837faa152d84d6b2d42838f0637a15eda8337dbdce";
|
|
||||||
|
|
||||||
let inserted_password_reset_request =
|
let inserted_password_reset_request =
|
||||||
PasswordResetRequest::create_token(pool, inserted_local_user.id, token)
|
PasswordResetRequest::create_token(pool, inserted_local_user.id, token.to_string())
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let expected_password_reset_request = PasswordResetRequest {
|
let expected_password_reset_request = PasswordResetRequest {
|
||||||
id: inserted_password_reset_request.id,
|
id: inserted_password_reset_request.id,
|
||||||
local_user_id: inserted_local_user.id,
|
local_user_id: inserted_local_user.id,
|
||||||
token_encrypted: token_encrypted_.to_string(),
|
token: token.to_string(),
|
||||||
published: inserted_password_reset_request.published,
|
published: inserted_password_reset_request.published,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,7 @@ impl Crud for Person {
|
||||||
.first::<Self>(conn)
|
.first::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
async fn delete(pool: &mut DbPool<'_>, person_id: PersonId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(person::table.find(person_id))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &PersonInsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &PersonInsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
insert_into(person::table)
|
insert_into(person::table)
|
||||||
|
|
|
@ -13,13 +13,6 @@ impl Crud for PersonMention {
|
||||||
type InsertForm = PersonMentionInsertForm;
|
type InsertForm = PersonMentionInsertForm;
|
||||||
type UpdateForm = PersonMentionUpdateForm;
|
type UpdateForm = PersonMentionUpdateForm;
|
||||||
type IdType = PersonMentionId;
|
type IdType = PersonMentionId;
|
||||||
async fn read(pool: &mut DbPool<'_>, person_mention_id: PersonMentionId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
person_mention
|
|
||||||
.find(person_mention_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(
|
async fn create(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
|
|
|
@ -38,15 +38,6 @@ impl Crud for Post {
|
||||||
type InsertForm = PostInsertForm;
|
type InsertForm = PostInsertForm;
|
||||||
type UpdateForm = PostUpdateForm;
|
type UpdateForm = PostUpdateForm;
|
||||||
type IdType = PostId;
|
type IdType = PostId;
|
||||||
async fn read(pool: &mut DbPool<'_>, post_id: PostId) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
post.find(post_id).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, post_id: PostId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(post.find(post_id)).execute(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
|
|
@ -15,16 +15,6 @@ impl Crud for PrivateMessage {
|
||||||
type InsertForm = PrivateMessageInsertForm;
|
type InsertForm = PrivateMessageInsertForm;
|
||||||
type UpdateForm = PrivateMessageUpdateForm;
|
type UpdateForm = PrivateMessageUpdateForm;
|
||||||
type IdType = PrivateMessageId;
|
type IdType = PrivateMessageId;
|
||||||
async fn read(
|
|
||||||
pool: &mut DbPool<'_>,
|
|
||||||
private_message_id: PrivateMessageId,
|
|
||||||
) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
private_message
|
|
||||||
.find(private_message_id)
|
|
||||||
.first::<Self>(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error> {
|
||||||
let conn = &mut get_conn(pool).await?;
|
let conn = &mut get_conn(pool).await?;
|
||||||
|
@ -48,12 +38,6 @@ impl Crud for PrivateMessage {
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
async fn delete(pool: &mut DbPool<'_>, pm_id: Self::IdType) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(private_message.find(pm_id))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrivateMessage {
|
impl PrivateMessage {
|
||||||
|
|
|
@ -26,11 +26,6 @@ impl Crud for RegistrationApplication {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn read(pool: &mut DbPool<'_>, id_: Self::IdType) -> Result<Self, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
registration_application.find(id_).first::<Self>(conn).await
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn update(
|
async fn update(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
id_: Self::IdType,
|
id_: Self::IdType,
|
||||||
|
@ -42,13 +37,6 @@ impl Crud for RegistrationApplication {
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, id_: Self::IdType) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(registration_application.find(id_))
|
|
||||||
.execute(conn)
|
|
||||||
.await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RegistrationApplication {
|
impl RegistrationApplication {
|
||||||
|
|
|
@ -58,11 +58,6 @@ impl Crud for Site {
|
||||||
.get_result::<Self>(conn)
|
.get_result::<Self>(conn)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn delete(pool: &mut DbPool<'_>, site_id: SiteId) -> Result<usize, Error> {
|
|
||||||
let conn = &mut get_conn(pool).await?;
|
|
||||||
diesel::delete(site.find(site_id)).execute(conn).await
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Site {
|
impl Site {
|
||||||
|
|
|
@ -548,7 +548,7 @@ diesel::table! {
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
password_reset_request (id) {
|
password_reset_request (id) {
|
||||||
id -> Int4,
|
id -> Int4,
|
||||||
token_encrypted -> Text,
|
token -> Text,
|
||||||
published -> Timestamp,
|
published -> Timestamp,
|
||||||
local_user_id -> Int4,
|
local_user_id -> Int4,
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use crate::schema::password_reset_request;
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
|
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
|
||||||
pub struct PasswordResetRequest {
|
pub struct PasswordResetRequest {
|
||||||
pub id: i32,
|
pub id: i32,
|
||||||
pub token_encrypted: String,
|
pub token: String,
|
||||||
pub published: chrono::NaiveDateTime,
|
pub published: chrono::NaiveDateTime,
|
||||||
pub local_user_id: LocalUserId,
|
pub local_user_id: LocalUserId,
|
||||||
}
|
}
|
||||||
|
@ -16,5 +16,5 @@ pub struct PasswordResetRequest {
|
||||||
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
|
#[cfg_attr(feature = "full", diesel(table_name = password_reset_request))]
|
||||||
pub struct PasswordResetRequestForm {
|
pub struct PasswordResetRequestForm {
|
||||||
pub local_user_id: LocalUserId,
|
pub local_user_id: LocalUserId,
|
||||||
pub token_encrypted: String,
|
pub token: String,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,34 +1,64 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
newtypes::{CommunityId, DbUrl, PersonId},
|
newtypes::{CommunityId, DbUrl, PersonId},
|
||||||
utils::DbPool,
|
utils::{get_conn, DbPool},
|
||||||
|
};
|
||||||
|
use diesel::{
|
||||||
|
associations::HasTable,
|
||||||
|
dsl,
|
||||||
|
query_builder::{DeleteStatement, IntoUpdateTarget},
|
||||||
|
query_dsl::methods::{FindDsl, LimitDsl},
|
||||||
|
result::Error,
|
||||||
|
Table,
|
||||||
|
};
|
||||||
|
use diesel_async::{
|
||||||
|
methods::{ExecuteDsl, LoadQuery},
|
||||||
|
AsyncPgConnection,
|
||||||
|
RunQueryDsl,
|
||||||
};
|
};
|
||||||
use diesel::result::Error;
|
|
||||||
|
|
||||||
|
/// Returned by `diesel::delete`
|
||||||
|
pub type Delete<T> = DeleteStatement<<T as HasTable>::Table, <T as IntoUpdateTarget>::WhereClause>;
|
||||||
|
|
||||||
|
/// Returned by `Self::table().find(id)`
|
||||||
|
pub type Find<T> = dsl::Find<<T as HasTable>::Table, <T as Crud>::IdType>;
|
||||||
|
|
||||||
|
pub type PrimaryKey<T> = <<T as HasTable>::Table as Table>::PrimaryKey;
|
||||||
|
|
||||||
|
// Trying to create default implementations for `create` and `update` results in a lifetime mess and weird compile errors.
|
||||||
|
// https://github.com/rust-lang/rust/issues/102211
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
pub trait Crud {
|
pub trait Crud: HasTable + Sized
|
||||||
|
where
|
||||||
|
Self::Table: FindDsl<Self::IdType>,
|
||||||
|
Find<Self>: LimitDsl + IntoUpdateTarget + Send,
|
||||||
|
Delete<Find<Self>>: ExecuteDsl<AsyncPgConnection> + Send + 'static,
|
||||||
|
|
||||||
|
// Used by `RunQueryDsl::first`
|
||||||
|
dsl::Limit<Find<Self>>: LoadQuery<'static, AsyncPgConnection, Self> + Send + 'static,
|
||||||
|
{
|
||||||
type InsertForm;
|
type InsertForm;
|
||||||
type UpdateForm;
|
type UpdateForm;
|
||||||
type IdType;
|
type IdType: Send;
|
||||||
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error>
|
|
||||||
where
|
async fn create(pool: &mut DbPool<'_>, form: &Self::InsertForm) -> Result<Self, Error>;
|
||||||
Self: Sized;
|
|
||||||
async fn read(pool: &mut DbPool<'_>, id: Self::IdType) -> Result<Self, Error>
|
async fn read(pool: &mut DbPool<'_>, id: Self::IdType) -> Result<Self, Error> {
|
||||||
where
|
let query: Find<Self> = Self::table().find(id);
|
||||||
Self: Sized;
|
let conn = &mut *get_conn(pool).await?;
|
||||||
|
query.first::<Self>(conn).await
|
||||||
|
}
|
||||||
|
|
||||||
/// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
|
/// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column.
|
||||||
async fn update(
|
async fn update(
|
||||||
pool: &mut DbPool<'_>,
|
pool: &mut DbPool<'_>,
|
||||||
id: Self::IdType,
|
id: Self::IdType,
|
||||||
form: &Self::UpdateForm,
|
form: &Self::UpdateForm,
|
||||||
) -> Result<Self, Error>
|
) -> Result<Self, Error>;
|
||||||
where
|
|
||||||
Self: Sized;
|
async fn delete(pool: &mut DbPool<'_>, id: Self::IdType) -> Result<usize, Error> {
|
||||||
async fn delete(_pool: &mut DbPool<'_>, _id: Self::IdType) -> Result<usize, Error>
|
let query: Delete<Find<Self>> = diesel::delete(Self::table().find(id));
|
||||||
where
|
let conn = &mut *get_conn(pool).await?;
|
||||||
Self: Sized,
|
query.execute(conn).await
|
||||||
Self::IdType: Send,
|
|
||||||
{
|
|
||||||
async { Err(Error::NotFound) }.await
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -308,7 +308,10 @@ fn queries<'a>() -> Queries<
|
||||||
.map(|l| l.local_user.show_read_posts)
|
.map(|l| l.local_user.show_read_posts)
|
||||||
.unwrap_or(true)
|
.unwrap_or(true)
|
||||||
{
|
{
|
||||||
query = query.filter(post_read::post_id.is_null());
|
// Do not hide read posts when it is a user profile view
|
||||||
|
if !is_profile_view {
|
||||||
|
query = query.filter(post_read::post_id.is_null());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.local_user.is_some() {
|
if options.local_user.is_some() {
|
||||||
|
|
|
@ -48,7 +48,9 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let open_registrations = Some(site_view.local_site.registration_mode == RegistrationMode::Open);
|
// Since there are 3 registration options,
|
||||||
|
// we need to set open_registrations as true if RegistrationMode is not Closed.
|
||||||
|
let open_registrations = Some(site_view.local_site.registration_mode != RegistrationMode::Closed);
|
||||||
let json = NodeInfo {
|
let json = NodeInfo {
|
||||||
version: Some("2.0".to_string()),
|
version: Some("2.0".to_string()),
|
||||||
software: Some(NodeInfoSoftware {
|
software: Some(NodeInfoSoftware {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
-- This file was automatically created by Diesel to setup helper functions
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
-- changes will be added to existing projects as new migrations.
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
DROP FUNCTION IF EXISTS diesel_manage_updated_at (_tbl regclass);
|
||||||
|
|
||||||
|
DROP FUNCTION IF EXISTS diesel_set_updated_at ();
|
||||||
|
|
||||||
DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass);
|
|
||||||
DROP FUNCTION IF EXISTS diesel_set_updated_at();
|
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
-- This file was automatically created by Diesel to setup helper functions
|
-- This file was automatically created by Diesel to setup helper functions
|
||||||
-- and other internal bookkeeping. This file is safe to edit, any future
|
-- and other internal bookkeeping. This file is safe to edit, any future
|
||||||
-- changes will be added to existing projects as new migrations.
|
-- changes will be added to existing projects as new migrations.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- Sets up a trigger for the given table to automatically set a column called
|
-- Sets up a trigger for the given table to automatically set a column called
|
||||||
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
-- `updated_at` whenever the row is modified (unless `updated_at` was included
|
||||||
-- in the modified columns)
|
-- in the modified columns)
|
||||||
|
@ -16,21 +12,25 @@
|
||||||
--
|
--
|
||||||
-- SELECT diesel_manage_updated_at('users');
|
-- SELECT diesel_manage_updated_at('users');
|
||||||
-- ```
|
-- ```
|
||||||
CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$
|
CREATE OR REPLACE FUNCTION diesel_manage_updated_at (_tbl regclass)
|
||||||
|
RETURNS VOID
|
||||||
|
AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s
|
||||||
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl);
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$
|
CREATE OR REPLACE FUNCTION diesel_set_updated_at ()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
AS $$
|
||||||
BEGIN
|
BEGIN
|
||||||
IF (
|
IF (NEW IS DISTINCT FROM OLD AND NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at) THEN
|
||||||
NEW IS DISTINCT FROM OLD AND
|
NEW.updated_at := CURRENT_TIMESTAMP;
|
||||||
NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at
|
|
||||||
) THEN
|
|
||||||
NEW.updated_at := current_timestamp;
|
|
||||||
END IF;
|
END IF;
|
||||||
RETURN NEW;
|
RETURN NEW;
|
||||||
END;
|
END;
|
||||||
$$ LANGUAGE plpgsql;
|
$$
|
||||||
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
drop table user_ban;
|
DROP TABLE user_ban;
|
||||||
drop table user_;
|
|
||||||
|
DROP TABLE user_;
|
||||||
|
|
||||||
|
|
|
@ -1,23 +1,25 @@
|
||||||
create table user_ (
|
CREATE TABLE user_ (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
name varchar(20) not null,
|
name varchar(20) NOT NULL,
|
||||||
fedi_name varchar(40) not null,
|
fedi_name varchar(40) NOT NULL,
|
||||||
preferred_username varchar(20),
|
preferred_username varchar(20),
|
||||||
password_encrypted text not null,
|
password_encrypted text NOT NULL,
|
||||||
email text unique,
|
email text UNIQUE,
|
||||||
icon bytea,
|
icon bytea,
|
||||||
admin boolean default false not null,
|
admin boolean DEFAULT FALSE NOT NULL,
|
||||||
banned boolean default false not null,
|
banned boolean DEFAULT FALSE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
updated timestamp,
|
updated timestamp,
|
||||||
unique(name, fedi_name)
|
UNIQUE (name, fedi_name)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table user_ban (
|
CREATE TABLE user_ban (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique (user_id)
|
UNIQUE (user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
insert into user_ (name, fedi_name, password_encrypted) values ('admin', 'TBD', 'TBD');
|
INSERT INTO user_ (name, fedi_name, password_encrypted)
|
||||||
|
VALUES ('admin', 'TBD', 'TBD');
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
drop table site;
|
DROP TABLE site;
|
||||||
drop table community_user_ban;;
|
|
||||||
drop table community_moderator;
|
DROP TABLE community_user_ban;
|
||||||
drop table community_follower;
|
|
||||||
drop table community;
|
;
|
||||||
drop table category;
|
|
||||||
|
DROP TABLE community_moderator;
|
||||||
|
|
||||||
|
DROP TABLE community_follower;
|
||||||
|
|
||||||
|
DROP TABLE community;
|
||||||
|
|
||||||
|
DROP TABLE category;
|
||||||
|
|
||||||
|
|
|
@ -1,79 +1,81 @@
|
||||||
create table category (
|
CREATE TABLE category (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
name varchar(100) not null unique
|
name varchar(100) NOT NULL UNIQUE
|
||||||
);
|
);
|
||||||
|
|
||||||
insert into category (name) values
|
INSERT INTO category (name)
|
||||||
('Discussion'),
|
VALUES ('Discussion'),
|
||||||
('Humor/Memes'),
|
('Humor/Memes'),
|
||||||
('Gaming'),
|
('Gaming'),
|
||||||
('Movies'),
|
('Movies'),
|
||||||
('TV'),
|
('TV'),
|
||||||
('Music'),
|
('Music'),
|
||||||
('Literature'),
|
('Literature'),
|
||||||
('Comics'),
|
('Comics'),
|
||||||
('Photography'),
|
('Photography'),
|
||||||
('Art'),
|
('Art'),
|
||||||
('Learning'),
|
('Learning'),
|
||||||
('DIY'),
|
('DIY'),
|
||||||
('Lifestyle'),
|
('Lifestyle'),
|
||||||
('News'),
|
('News'),
|
||||||
('Politics'),
|
('Politics'),
|
||||||
('Society'),
|
('Society'),
|
||||||
('Gender/Identity/Sexuality'),
|
('Gender/Identity/Sexuality'),
|
||||||
('Race/Colonisation'),
|
('Race/Colonisation'),
|
||||||
('Religion'),
|
('Religion'),
|
||||||
('Science/Technology'),
|
('Science/Technology'),
|
||||||
('Programming/Software'),
|
('Programming/Software'),
|
||||||
('Health/Sports/Fitness'),
|
('Health/Sports/Fitness'),
|
||||||
('Porn'),
|
('Porn'),
|
||||||
('Places'),
|
('Places'),
|
||||||
('Meta'),
|
('Meta'),
|
||||||
('Other');
|
('Other');
|
||||||
|
|
||||||
create table community (
|
CREATE TABLE community (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
name varchar(20) not null unique,
|
name varchar(20) NOT NULL UNIQUE,
|
||||||
title varchar(100) not null,
|
title varchar(100) NOT NULL,
|
||||||
description text,
|
description text,
|
||||||
category_id int references category on update cascade on delete cascade not null,
|
category_id int REFERENCES category ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
removed boolean default false not null,
|
removed boolean DEFAULT FALSE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
create table community_moderator (
|
CREATE TABLE community_moderator (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
community_id int references community on update cascade on delete cascade not null,
|
community_id int REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique (community_id, user_id)
|
UNIQUE (community_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table community_follower (
|
CREATE TABLE community_follower (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
community_id int references community on update cascade on delete cascade not null,
|
community_id int REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique (community_id, user_id)
|
UNIQUE (community_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table community_user_ban (
|
CREATE TABLE community_user_ban (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
community_id int references community on update cascade on delete cascade not null,
|
community_id int REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique (community_id, user_id)
|
UNIQUE (community_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
insert into community (name, title, category_id, creator_id) values ('main', 'The Default Community', 1, 1);
|
INSERT INTO community (name, title, category_id, creator_id)
|
||||||
|
VALUES ('main', 'The Default Community', 1, 1);
|
||||||
|
|
||||||
create table site (
|
CREATE TABLE site (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
name varchar(20) not null unique,
|
name varchar(20) NOT NULL UNIQUE,
|
||||||
description text,
|
description text,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
drop table post_read;
|
DROP TABLE post_read;
|
||||||
drop table post_saved;
|
|
||||||
drop table post_like;
|
DROP TABLE post_saved;
|
||||||
drop table post;
|
|
||||||
|
DROP TABLE post_like;
|
||||||
|
|
||||||
|
DROP TABLE post;
|
||||||
|
|
||||||
|
|
|
@ -1,37 +1,38 @@
|
||||||
create table post (
|
CREATE TABLE post (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
name varchar(100) not null,
|
name varchar(100) NOT NULL,
|
||||||
url text, -- These are both optional, a post can just have a title
|
url text, -- These are both optional, a post can just have a title
|
||||||
body text,
|
body text,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
community_id int references community on update cascade on delete cascade not null,
|
community_id int REFERENCES community ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
removed boolean default false not null,
|
removed boolean DEFAULT FALSE NOT NULL,
|
||||||
locked boolean default false not null,
|
locked boolean DEFAULT FALSE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
create table post_like (
|
CREATE TABLE post_like (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
score smallint not null, -- -1, or 1 for dislike, like, no row for no opinion
|
score smallint NOT NULL, -- -1, or 1 for dislike, like, no row for no opinion
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique(post_id, user_id)
|
UNIQUE (post_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table post_saved (
|
CREATE TABLE post_saved (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique(post_id, user_id)
|
UNIQUE (post_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table post_read (
|
CREATE TABLE post_read (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique(post_id, user_id)
|
UNIQUE (post_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
drop table comment_saved;
|
DROP TABLE comment_saved;
|
||||||
drop table comment_like;
|
|
||||||
drop table comment;
|
DROP TABLE comment_like;
|
||||||
|
|
||||||
|
DROP TABLE comment;
|
||||||
|
|
||||||
|
|
|
@ -1,29 +1,30 @@
|
||||||
create table comment (
|
CREATE TABLE comment (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
creator_id int references user_ on update cascade on delete cascade not null,
|
creator_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
parent_id int references comment on update cascade on delete cascade,
|
parent_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE,
|
||||||
content text not null,
|
content text NOT NULL,
|
||||||
removed boolean default false not null,
|
removed boolean DEFAULT FALSE NOT NULL,
|
||||||
read boolean default false not null,
|
read boolean DEFAULT FALSE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
updated timestamp
|
updated timestamp
|
||||||
);
|
);
|
||||||
|
|
||||||
create table comment_like (
|
CREATE TABLE comment_like (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
comment_id int references comment on update cascade on delete cascade not null,
|
comment_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
post_id int references post on update cascade on delete cascade not null,
|
post_id int REFERENCES post ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
score smallint not null, -- -1, or 1 for dislike, like, no row for no opinion
|
score smallint NOT NULL, -- -1, or 1 for dislike, like, no row for no opinion
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique(comment_id, user_id)
|
UNIQUE (comment_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
create table comment_saved (
|
CREATE TABLE comment_saved (
|
||||||
id serial primary key,
|
id serial PRIMARY KEY,
|
||||||
comment_id int references comment on update cascade on delete cascade not null,
|
comment_id int REFERENCES COMMENT ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
user_id int references user_ on update cascade on delete cascade not null,
|
user_id int REFERENCES user_ ON UPDATE CASCADE ON DELETE CASCADE NOT NULL,
|
||||||
published timestamp not null default now(),
|
published timestamp NOT NULL DEFAULT now(),
|
||||||
unique(comment_id, user_id)
|
UNIQUE (comment_id, user_id)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
drop view post_view;
|
DROP VIEW post_view;
|
||||||
drop function hot_rank;
|
|
||||||
|
DROP FUNCTION hot_rank;
|
||||||
|
|
||||||
|
|
|
@ -1,51 +1,107 @@
|
||||||
-- Rank = ScaleFactor * sign(Score) * log(1 + abs(Score)) / (Time + 2)^Gravity
|
-- Rank = ScaleFactor * sign(Score) * log(1 + abs(Score)) / (Time + 2)^Gravity
|
||||||
create or replace function hot_rank(
|
CREATE OR REPLACE FUNCTION hot_rank (score numeric, published timestamp without time zone)
|
||||||
score numeric,
|
RETURNS integer
|
||||||
published timestamp without time zone)
|
AS $$
|
||||||
returns integer as $$
|
BEGIN
|
||||||
begin
|
-- hours_diff:=EXTRACT(EPOCH FROM (timezone('utc',now()) - published))/3600
|
||||||
-- hours_diff:=EXTRACT(EPOCH FROM (timezone('utc',now()) - published))/3600
|
RETURN floor(10000 * log(greatest (1, score + 3)) / power(((EXTRACT(EPOCH FROM (timezone('utc', now()) - published)) / 3600) + 2), 1.8))::integer;
|
||||||
return floor(10000*log(greatest(1,score+3)) / power(((EXTRACT(EPOCH FROM (timezone('utc',now()) - published))/3600) + 2), 1.8))::integer;
|
END;
|
||||||
end; $$
|
$$
|
||||||
LANGUAGE plpgsql;
|
LANGUAGE plpgsql;
|
||||||
|
|
||||||
create view post_view as
|
CREATE VIEW post_view AS
|
||||||
with all_post as
|
with all_post AS (
|
||||||
(
|
SELECT
|
||||||
select
|
p.*,
|
||||||
p.*,
|
(
|
||||||
(select name from user_ where p.creator_id = user_.id) as creator_name,
|
SELECT
|
||||||
(select name from community where p.community_id = community.id) as community_name,
|
name
|
||||||
(select removed from community c where p.community_id = c.id) as community_removed,
|
FROM
|
||||||
(select count(*) from comment where comment.post_id = p.id) as number_of_comments,
|
user_
|
||||||
coalesce(sum(pl.score), 0) as score,
|
WHERE
|
||||||
count (case when pl.score = 1 then 1 else null end) as upvotes,
|
p.creator_id = user_.id) AS creator_name,
|
||||||
count (case when pl.score = -1 then 1 else null end) as downvotes,
|
(
|
||||||
hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank
|
SELECT
|
||||||
from post p
|
name
|
||||||
left join post_like pl on p.id = pl.post_id
|
FROM
|
||||||
group by p.id
|
community
|
||||||
|
WHERE
|
||||||
|
p.community_id = community.id) AS community_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
removed
|
||||||
|
FROM
|
||||||
|
community c
|
||||||
|
WHERE
|
||||||
|
p.community_id = c.id) AS community_removed,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
comment
|
||||||
|
WHERE
|
||||||
|
comment.post_id = p.id) AS number_of_comments,
|
||||||
|
coalesce(sum(pl.score), 0) AS score,
|
||||||
|
count(
|
||||||
|
CASE WHEN pl.score = 1 THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
NULL
|
||||||
|
END) AS upvotes,
|
||||||
|
count(
|
||||||
|
CASE WHEN pl.score = - 1 THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
NULL
|
||||||
|
END) AS downvotes,
|
||||||
|
hot_rank (coalesce(sum(pl.score), 0), p.published) AS hot_rank
|
||||||
|
FROM
|
||||||
|
post p
|
||||||
|
LEFT JOIN post_like pl ON p.id = pl.post_id
|
||||||
|
GROUP BY
|
||||||
|
p.id
|
||||||
)
|
)
|
||||||
|
SELECT
|
||||||
|
ap.*,
|
||||||
|
u.id AS user_id,
|
||||||
|
coalesce(pl.score, 0) AS my_vote,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
cf.id::bool
|
||||||
|
FROM
|
||||||
|
community_follower cf
|
||||||
|
WHERE
|
||||||
|
u.id = cf.user_id
|
||||||
|
AND cf.community_id = ap.community_id) AS subscribed,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
pr.id::bool
|
||||||
|
FROM
|
||||||
|
post_read pr
|
||||||
|
WHERE
|
||||||
|
u.id = pr.user_id
|
||||||
|
AND pr.post_id = ap.id) AS read,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
ps.id::bool
|
||||||
|
FROM
|
||||||
|
post_saved ps
|
||||||
|
WHERE
|
||||||
|
u.id = ps.user_id
|
||||||
|
AND ps.post_id = ap.id) AS saved
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
CROSS JOIN all_post ap
|
||||||
|
LEFT JOIN post_like pl ON u.id = pl.user_id
|
||||||
|
AND ap.id = pl.post_id
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
ap.*,
|
||||||
|
NULL AS user_id,
|
||||||
|
NULL AS my_vote,
|
||||||
|
NULL AS subscribed,
|
||||||
|
NULL AS read,
|
||||||
|
NULL AS saved
|
||||||
|
FROM
|
||||||
|
all_post ap;
|
||||||
|
|
||||||
select
|
|
||||||
ap.*,
|
|
||||||
u.id as user_id,
|
|
||||||
coalesce(pl.score, 0) as my_vote,
|
|
||||||
(select cf.id::bool from community_follower cf where u.id = cf.user_id and cf.community_id = ap.community_id) as subscribed,
|
|
||||||
(select pr.id::bool from post_read pr where u.id = pr.user_id and pr.post_id = ap.id) as read,
|
|
||||||
(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved
|
|
||||||
from user_ u
|
|
||||||
cross join all_post ap
|
|
||||||
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id
|
|
||||||
|
|
||||||
union all
|
|
||||||
|
|
||||||
select
|
|
||||||
ap.*,
|
|
||||||
null as user_id,
|
|
||||||
null as my_vote,
|
|
||||||
null as subscribed,
|
|
||||||
null as read,
|
|
||||||
null as saved
|
|
||||||
from all_post ap
|
|
||||||
;
|
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
drop view community_view;
|
DROP VIEW community_view;
|
||||||
drop view community_moderator_view;
|
|
||||||
drop view community_follower_view;
|
DROP VIEW community_moderator_view;
|
||||||
drop view community_user_ban_view;
|
|
||||||
drop view site_view;
|
DROP VIEW community_follower_view;
|
||||||
|
|
||||||
|
DROP VIEW community_user_ban_view;
|
||||||
|
|
||||||
|
DROP VIEW site_view;
|
||||||
|
|
||||||
|
|
|
@ -1,53 +1,154 @@
|
||||||
create view community_view as
|
CREATE VIEW community_view AS
|
||||||
with all_community as
|
with all_community AS (
|
||||||
(
|
SELECT
|
||||||
select *,
|
*,
|
||||||
(select name from user_ u where c.creator_id = u.id) as creator_name,
|
(
|
||||||
(select name from category ct where c.category_id = ct.id) as category_name,
|
SELECT
|
||||||
(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers,
|
name
|
||||||
(select count(*) from post p where p.community_id = c.id) as number_of_posts,
|
FROM
|
||||||
(select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments
|
user_ u
|
||||||
from community c
|
WHERE
|
||||||
|
c.creator_id = u.id) AS creator_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
category ct
|
||||||
|
WHERE
|
||||||
|
c.category_id = ct.id) AS category_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
community_follower cf
|
||||||
|
WHERE
|
||||||
|
cf.community_id = c.id) AS number_of_subscribers,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
post p
|
||||||
|
WHERE
|
||||||
|
p.community_id = c.id) AS number_of_posts,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
comment co,
|
||||||
|
post p
|
||||||
|
WHERE
|
||||||
|
c.id = p.community_id
|
||||||
|
AND p.id = co.post_id) AS number_of_comments
|
||||||
|
FROM
|
||||||
|
community c
|
||||||
)
|
)
|
||||||
|
SELECT
|
||||||
|
ac.*,
|
||||||
|
u.id AS user_id,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
cf.id::boolean
|
||||||
|
FROM
|
||||||
|
community_follower cf
|
||||||
|
WHERE
|
||||||
|
u.id = cf.user_id
|
||||||
|
AND ac.id = cf.community_id) AS subscribed
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
CROSS JOIN all_community ac
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
ac.*,
|
||||||
|
NULL AS user_id,
|
||||||
|
NULL AS subscribed
|
||||||
|
FROM
|
||||||
|
all_community ac;
|
||||||
|
|
||||||
select
|
CREATE VIEW community_moderator_view AS
|
||||||
ac.*,
|
SELECT
|
||||||
u.id as user_id,
|
*,
|
||||||
(select cf.id::boolean from community_follower cf where u.id = cf.user_id and ac.id = cf.community_id) as subscribed
|
(
|
||||||
from user_ u
|
SELECT
|
||||||
cross join all_community ac
|
name
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
WHERE
|
||||||
|
cm.user_id = u.id) AS user_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
community c
|
||||||
|
WHERE
|
||||||
|
cm.community_id = c.id) AS community_name
|
||||||
|
FROM
|
||||||
|
community_moderator cm;
|
||||||
|
|
||||||
union all
|
CREATE VIEW community_follower_view AS
|
||||||
|
SELECT
|
||||||
|
*,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
WHERE
|
||||||
|
cf.user_id = u.id) AS user_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
community c
|
||||||
|
WHERE
|
||||||
|
cf.community_id = c.id) AS community_name
|
||||||
|
FROM
|
||||||
|
community_follower cf;
|
||||||
|
|
||||||
select
|
CREATE VIEW community_user_ban_view AS
|
||||||
ac.*,
|
SELECT
|
||||||
null as user_id,
|
*,
|
||||||
null as subscribed
|
(
|
||||||
from all_community ac
|
SELECT
|
||||||
;
|
name
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
WHERE
|
||||||
|
cm.user_id = u.id) AS user_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
community c
|
||||||
|
WHERE
|
||||||
|
cm.community_id = c.id) AS community_name
|
||||||
|
FROM
|
||||||
|
community_user_ban cm;
|
||||||
|
|
||||||
create view community_moderator_view as
|
CREATE VIEW site_view AS
|
||||||
select *,
|
SELECT
|
||||||
(select name from user_ u where cm.user_id = u.id) as user_name,
|
*,
|
||||||
(select name from community c where cm.community_id = c.id) as community_name
|
(
|
||||||
from community_moderator cm;
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
WHERE
|
||||||
|
s.creator_id = u.id) AS creator_name,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
user_) AS number_of_users,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
post) AS number_of_posts,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
count(*)
|
||||||
|
FROM
|
||||||
|
comment) AS number_of_comments
|
||||||
|
FROM
|
||||||
|
site s;
|
||||||
|
|
||||||
create view community_follower_view as
|
|
||||||
select *,
|
|
||||||
(select name from user_ u where cf.user_id = u.id) as user_name,
|
|
||||||
(select name from community c where cf.community_id = c.id) as community_name
|
|
||||||
from community_follower cf;
|
|
||||||
|
|
||||||
create view community_user_ban_view as
|
|
||||||
select *,
|
|
||||||
(select name from user_ u where cm.user_id = u.id) as user_name,
|
|
||||||
(select name from community c where cm.community_id = c.id) as community_name
|
|
||||||
from community_user_ban cm;
|
|
||||||
|
|
||||||
create view site_view as
|
|
||||||
select *,
|
|
||||||
(select name from user_ u where s.creator_id = u.id) as creator_name,
|
|
||||||
(select count(*) from user_) as number_of_users,
|
|
||||||
(select count(*) from post) as number_of_posts,
|
|
||||||
(select count(*) from comment) as number_of_comments
|
|
||||||
from site s;
|
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
drop view reply_view;
|
DROP VIEW reply_view;
|
||||||
drop view comment_view;
|
|
||||||
|
DROP VIEW comment_view;
|
||||||
|
|
||||||
|
|
|
@ -1,60 +1,114 @@
|
||||||
create view comment_view as
|
CREATE VIEW comment_view AS
|
||||||
with all_comment as
|
with all_comment AS (
|
||||||
(
|
SELECT
|
||||||
select
|
c.*,
|
||||||
c.*,
|
(
|
||||||
(select community_id from post p where p.id = c.post_id),
|
SELECT
|
||||||
(select u.banned from user_ u where c.creator_id = u.id) as banned,
|
community_id
|
||||||
(select cb.id::bool from community_user_ban cb, post p where c.creator_id = cb.user_id and p.id = c.post_id and p.community_id = cb.community_id) as banned_from_community,
|
FROM
|
||||||
(select name from user_ where c.creator_id = user_.id) as creator_name,
|
post p
|
||||||
coalesce(sum(cl.score), 0) as score,
|
WHERE
|
||||||
count (case when cl.score = 1 then 1 else null end) as upvotes,
|
p.id = c.post_id),
|
||||||
count (case when cl.score = -1 then 1 else null end) as downvotes
|
(
|
||||||
from comment c
|
SELECT
|
||||||
left join comment_like cl on c.id = cl.comment_id
|
u.banned
|
||||||
group by c.id
|
FROM
|
||||||
|
user_ u
|
||||||
|
WHERE
|
||||||
|
c.creator_id = u.id) AS banned,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
cb.id::bool
|
||||||
|
FROM
|
||||||
|
community_user_ban cb,
|
||||||
|
post p
|
||||||
|
WHERE
|
||||||
|
c.creator_id = cb.user_id
|
||||||
|
AND p.id = c.post_id
|
||||||
|
AND p.community_id = cb.community_id) AS banned_from_community,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
name
|
||||||
|
FROM
|
||||||
|
user_
|
||||||
|
WHERE
|
||||||
|
c.creator_id = user_.id) AS creator_name,
|
||||||
|
coalesce(sum(cl.score), 0) AS score,
|
||||||
|
count(
|
||||||
|
CASE WHEN cl.score = 1 THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
NULL
|
||||||
|
END) AS upvotes,
|
||||||
|
count(
|
||||||
|
CASE WHEN cl.score = - 1 THEN
|
||||||
|
1
|
||||||
|
ELSE
|
||||||
|
NULL
|
||||||
|
END) AS downvotes
|
||||||
|
FROM
|
||||||
|
comment c
|
||||||
|
LEFT JOIN comment_like cl ON c.id = cl.comment_id
|
||||||
|
GROUP BY
|
||||||
|
c.id
|
||||||
)
|
)
|
||||||
|
SELECT
|
||||||
|
ac.*,
|
||||||
|
u.id AS user_id,
|
||||||
|
coalesce(cl.score, 0) AS my_vote,
|
||||||
|
(
|
||||||
|
SELECT
|
||||||
|
cs.id::bool
|
||||||
|
FROM
|
||||||
|
comment_saved cs
|
||||||
|
WHERE
|
||||||
|
u.id = cs.user_id
|
||||||
|
AND cs.comment_id = ac.id) AS saved
|
||||||
|
FROM
|
||||||
|
user_ u
|
||||||
|
CROSS JOIN all_comment ac
|
||||||
|
LEFT JOIN comment_like cl ON u.id = cl.user_id
|
||||||
|
AND ac.id = cl.comment_id
|
||||||
|
UNION ALL
|
||||||
|
SELECT
|
||||||
|
ac.*,
|
||||||
|
NULL AS user_id,
|
||||||
|
NULL AS my_vote,
|
||||||
|
NULL AS saved
|
||||||
|
FROM
|
||||||
|
all_comment ac;
|
||||||
|
|
||||||
select
|
CREATE VIEW reply_view AS
|
||||||
ac.*,
|
with closereply AS (
|
||||||
u.id as user_id,
|
SELECT
|
||||||
coalesce(cl.score, 0) as my_vote,
|
c2.id,
|
||||||
(select cs.id::bool from comment_saved cs where u.id = cs.user_id and cs.comment_id = ac.id) as saved
|
c2.creator_id AS sender_id,
|
||||||
from user_ u
|
c.creator_id AS recipient_id
|
||||||
cross join all_comment ac
|
FROM
|
||||||
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id
|
comment c
|
||||||
|
INNER JOIN comment c2 ON c.id = c2.parent_id
|
||||||
union all
|
WHERE
|
||||||
|
c2.creator_id != c.creator_id
|
||||||
select
|
-- Do union where post is null
|
||||||
ac.*,
|
UNION
|
||||||
null as user_id,
|
SELECT
|
||||||
null as my_vote,
|
c.id,
|
||||||
null as saved
|
c.creator_id AS sender_id,
|
||||||
from all_comment ac
|
p.creator_id AS recipient_id
|
||||||
;
|
FROM
|
||||||
|
comment c,
|
||||||
create view reply_view as
|
post p
|
||||||
with closereply as (
|
WHERE
|
||||||
select
|
c.post_id = p.id
|
||||||
c2.id,
|
AND c.parent_id IS NULL
|
||||||
c2.creator_id as sender_id,
|
AND c.creator_id != p.creator_id
|
||||||
c.creator_id as recipient_id
|
|
||||||
from comment c
|
|
||||||
inner join comment c2 on c.id = c2.parent_id
|
|
||||||
where c2.creator_id != c.creator_id
|
|
||||||
-- Do union where post is null
|
|
||||||
union
|
|
||||||
select
|
|
||||||
c.id,
|
|
||||||
c.creator_id as sender_id,
|
|
||||||
p.creator_id as recipient_id
|
|
||||||
from comment c, post p
|
|
||||||
where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id
|
|
||||||
)
|
)
|
||||||
select cv.*,
|
SELECT
|
||||||
closereply.recipient_id
|
cv.*,
|
||||||
from comment_view cv, closereply
|
closereply.recipient_id
|
||||||
where closereply.id = cv.id
|
FROM
|
||||||
;
|
comment_view cv,
|
||||||
|
closereply
|
||||||
|
WHERE
|
||||||
|
closereply.id = cv.id;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
drop table mod_remove_post;
|
DROP TABLE mod_remove_post;
|
||||||
drop table mod_lock_post;
|
|
||||||
drop table mod_remove_comment;
|
DROP TABLE mod_lock_post;
|
||||||
drop table mod_remove_community;
|
|
||||||
drop table mod_ban;
|
DROP TABLE mod_remove_comment;
|
||||||
drop table mod_ban_from_community;
|
|
||||||
drop table mod_add;
|
DROP TABLE mod_remove_community;
|
||||||
drop table mod_add_community;
|
|
||||||
|
DROP TABLE mod_ban;
|
||||||
|
|
||||||
|
DROP TABLE mod_ban_from_community;
|
||||||
|
|
||||||
|
DROP TABLE mod_add;
|
||||||
|
|
||||||
|
DROP TABLE mod_add_community;
|
||||||
|
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue