Merge remote-tracking branch 'origin/main' into persistent-queue

This commit is contained in:
phiresky 2023-08-02 23:01:50 +00:00
commit 2d3ad1b076
427 changed files with 25309 additions and 15179 deletions

View File

@ -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
View File

@ -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",

View File

@ -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"

View File

@ -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",

View File

@ -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"

View File

@ -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

View File

@ -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;

View File

@ -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,

View File

@ -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,16 +18,15 @@ 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)]
impl Perform for CreateCommentLike {
type Response = CommentResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { pub async fn like_comment(
let data: &CreateCommentLike = self; data: Json<CreateCommentLike>,
context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = LocalSite::read(&mut context.pool()).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let mut recipient_ids = Vec::<LocalUserId>::new(); let mut recipient_ids = Vec::<LocalUserId>::new();
@ -47,8 +47,7 @@ impl Perform for CreateCommentLike {
let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await; let comment_reply = CommentReply::read_by_comment(&mut context.pool(), comment_id).await;
if let Ok(reply) = comment_reply { if let Ok(reply) = comment_reply {
let recipient_id = reply.recipient_id; let recipient_id = reply.recipient_id;
if let Ok(local_recipient) = if let Ok(local_recipient) = LocalUserView::read_person(&mut context.pool(), recipient_id).await
LocalUserView::read_person(&mut context.pool(), recipient_id).await
{ {
recipient_ids.push(local_recipient.local_user.id); recipient_ids.push(local_recipient.local_user.id);
} }
@ -74,13 +73,25 @@ impl Perform for CreateCommentLike {
.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?,
} ))
} }

View File

@ -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,20 +23,15 @@ 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)]
impl Perform for CreateCommentReport {
type Response = CommentReportResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform( pub async fn create_comment_report(
&self, data: Json<CreateCommentReport>,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<CommentReportResponse, LemmyError> { ) -> Result<Json<CommentReportResponse>, LemmyError> {
let data: &CreateCommentReport = self; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = LocalSite::read(&mut context.pool()).await?;
let reason = sanitize_html(self.reason.trim()); let reason = sanitize_html(data.reason.trim());
check_report_reason(&reason, &local_site)?; check_report_reason(&reason, &local_site)?;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
@ -68,8 +65,18 @@ impl Perform for CreateCommentReport {
.await?; .await?;
} }
Ok(CommentReportResponse { 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, comment_report_view,
}) }))
}
} }

View File

@ -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,17 +16,12 @@ 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)]
impl Perform for AddModToCommunity {
type Response = AddModToCommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform( pub async fn add_mod_to_community(
&self, data: Json<AddModToCommunity>,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<AddModToCommunityResponse, LemmyError> { ) -> Result<Json<AddModToCommunityResponse>, LemmyError> {
let data: &AddModToCommunity = self; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let community_id = data.community_id; let community_id = data.community_id;
@ -64,9 +60,18 @@ impl Perform for AddModToCommunity {
// Note: in case a remote mod is added, this returns the old moderators list, it will only get // 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`) // updated once we receive an activity from the community (like `Announce/Add/Moderator`)
let community_id = data.community_id; let community_id = data.community_id;
let moderators = let moderators = CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
CommunityModeratorView::for_community(&mut context.pool(), community_id).await?;
Ok(AddModToCommunityResponse { moderators }) ActivityChannel::submit_activity(
} SendActivityData::AddModToCommunity(
local_user_view.person,
data.community_id,
data.person_id,
data.added,
),
&context,
)
.await?;
Ok(Json(AddModToCommunityResponse { moderators }))
} }

View File

@ -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,25 +29,24 @@ 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)]
impl Perform for BanFromCommunity {
type Response = BanFromCommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform( pub async fn ban_from_community(
&self, data: Json<BanFromCommunity>,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<BanFromCommunityResponse, LemmyError> { ) -> Result<Json<BanFromCommunityResponse>, LemmyError> {
let data: &BanFromCommunity = self; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let community_id = data.community_id;
let banned_person_id = data.person_id; let banned_person_id = data.person_id;
let remove_data = data.remove_data.unwrap_or(false); let remove_data = data.remove_data.unwrap_or(false);
let expires = data.expires.map(naive_from_unix); let expires = data.expires.map(naive_from_unix);
// Verify that only mods or admins can ban // Verify that only mods or admins can ban
is_mod_or_admin(&mut context.pool(), local_user_view.person.id, community_id).await?; is_mod_or_admin(
&mut context.pool(),
local_user_view.person.id,
data.community_id,
)
.await?;
is_valid_body_field(&data.reason, false)?; is_valid_body_field(&data.reason, false)?;
let community_user_ban_form = CommunityPersonBanForm { let community_user_ban_form = CommunityPersonBanForm {
@ -78,7 +78,7 @@ impl Perform for BanFromCommunity {
// Remove/Restore their data if that's desired // Remove/Restore their data if that's desired
if remove_data { if remove_data {
remove_user_data_in_community(community_id, banned_person_id, &mut context.pool()).await?; remove_user_data_in_community(data.community_id, banned_person_id, &mut context.pool()).await?;
} }
// Mod tables // Mod tables
@ -93,12 +93,21 @@ impl Perform for BanFromCommunity {
ModBanFromCommunity::create(&mut context.pool(), &form).await?; ModBanFromCommunity::create(&mut context.pool(), &form).await?;
let person_id = data.person_id; let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
let person_view = PersonView::read(&mut context.pool(), person_id).await?;
Ok(BanFromCommunityResponse { 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, person_view,
banned: data.ban, banned: data.ban,
}) }))
}
} }

View File

@ -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,17 +16,12 @@ 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)]
impl Perform for BlockCommunity {
type Response = BlockCommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform( pub async fn block_community(
&self, data: Json<BlockCommunity>,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<BlockCommunityResponse, LemmyError> { ) -> Result<Json<BlockCommunityResponse>, LemmyError> {
let data: &BlockCommunity = self; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let community_id = data.community_id; let community_id = data.community_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
@ -58,9 +54,18 @@ impl Perform for BlockCommunity {
let community_view = let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?; CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
Ok(BlockCommunityResponse { ActivityChannel::submit_activity(
SendActivityData::FollowCommunity(
community_view.community.clone(),
local_user_view.person.clone(),
false,
),
&context,
)
.await?;
Ok(Json(BlockCommunityResponse {
blocked: data.block, blocked: data.block,
community_view, community_view,
}) }))
}
} }

View File

@ -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,27 +16,24 @@ 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)]
impl Perform for FollowCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn follow_community(
let data: &FollowCommunity = self; data: Json<FollowCommunity>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<CommunityResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let community_id = data.community_id; let community = Community::read(&mut context.pool(), data.community_id).await?;
let community = Community::read(&mut context.pool(), community_id).await?;
let mut community_follower_form = CommunityFollowerForm { let mut community_follower_form = CommunityFollowerForm {
community_id: data.community_id, community_id: community.id,
person_id: local_user_view.person.id, person_id: local_user_view.person.id,
pending: false, pending: false,
}; };
if data.follow { if data.follow {
if community.local { if community.local {
check_community_ban(local_user_view.person.id, community_id, &mut context.pool()).await?; check_community_ban(local_user_view.person.id, community.id, &mut context.pool()).await?;
check_community_deleted_or_removed(community_id, &mut context.pool()).await?; check_community_deleted_or_removed(community.id, &mut context.pool()).await?;
CommunityFollower::follow(&mut context.pool(), &community_follower_form) CommunityFollower::follow(&mut context.pool(), &community_follower_form)
.await .await
@ -54,15 +52,20 @@ impl Perform for FollowCommunity {
.with_lemmy_type(LemmyErrorType::CommunityFollowerAlreadyExists)?; .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 community_id = data.community_id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let community_view = let community_view =
CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?; CommunityView::read(&mut context.pool(), community_id, Some(person_id), None).await?;
let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?; let discussion_languages = CommunityLanguage::read(&mut context.pool(), community_id).await?;
Ok(Self::Response { Ok(Json(CommunityResponse {
community_view, community_view,
discussion_languages, discussion_languages,
}) }))
}
} }

View File

@ -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,16 +16,13 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorExt, LemmyErrorType};
#[async_trait::async_trait(?Send)]
impl Perform for HideCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn hide_community(
let data: &HideCommunity = self; data: Json<HideCommunity>,
context: Data<LemmyContext>,
) -> Result<Json<CommunityResponse>, LemmyError> {
// Verify its a admin (only admin can hide or unhide it) // Verify its a admin (only admin can hide or unhide it)
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let community_form = CommunityUpdateForm::builder() let community_form = CommunityUpdateForm::builder()
@ -39,12 +37,17 @@ impl Perform for HideCommunity {
}; };
let community_id = data.community_id; let community_id = data.community_id;
Community::update(&mut context.pool(), community_id, &community_form) let community = Community::update(&mut context.pool(), community_id, &community_form)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunityHiddenStatus)?;
ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?; ModHideCommunity::create(&mut context.pool(), &mod_hide_community_form).await?;
build_community_response(context, local_user_view, community_id).await ActivityChannel::submit_activity(
} SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
&context,
)
.await?;
build_community_response(&context, local_user_view, community_id).await
} }

View File

@ -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;

View File

@ -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,30 +18,25 @@ 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},
}; };
#[async_trait::async_trait(?Send)]
impl Perform for BanPerson {
type Response = BanPersonResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<BanPersonResponse, LemmyError> { pub async fn ban_from_site(
let data: &BanPerson = self; data: Json<BanPerson>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<BanPersonResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
is_valid_body_field(&data.reason, false)?; is_valid_body_field(&data.reason, false)?;
let ban = data.ban;
let banned_person_id = data.person_id;
let expires = data.expires.map(naive_from_unix); let expires = data.expires.map(naive_from_unix);
let person = Person::update( let person = Person::update(
&mut context.pool(), &mut context.pool(),
banned_person_id, data.person_id,
&PersonUpdateForm::builder() &PersonUpdateForm::builder()
.banned(Some(ban)) .banned(Some(data.ban))
.ban_expires(Some(expires)) .ban_expires(Some(expires))
.build(), .build(),
) )
@ -70,12 +66,20 @@ impl Perform for BanPerson {
ModBan::create(&mut context.pool(), &form).await?; ModBan::create(&mut context.pool(), &form).await?;
let person_id = data.person_id; let person_view = PersonView::read(&mut context.pool(), data.person_id).await?;
let person_view = PersonView::read(&mut context.pool(), person_id).await?;
Ok(BanPersonResponse { ActivityChannel::submit_activity(
SendActivityData::BanFromSite(
local_user_view.person,
person_view.person.clone(),
data.0.clone(),
),
&context,
)
.await?;
Ok(Json(BanPersonResponse {
person_view, person_view,
banned: data.ban, banned: data.ban,
}) }))
}
} }

View File

@ -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,14 +23,12 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[async_trait::async_trait(?Send)]
impl Perform for FeaturePost {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn feature_post(
let data: &FeaturePost = self; data: Json<FeaturePost>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?; let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -65,7 +64,7 @@ impl Perform for FeaturePost {
.featured_local(Some(data.featured)) .featured_local(Some(data.featured))
.build() .build()
}; };
Post::update(&mut context.pool(), post_id, &new_post).await?; let post = Post::update(&mut context.pool(), post_id, &new_post).await?;
// Mod tables // Mod tables
let form = ModFeaturePostForm { let form = ModFeaturePostForm {
@ -77,12 +76,12 @@ impl Perform for FeaturePost {
ModFeaturePost::create(&mut context.pool(), &form).await?; ModFeaturePost::create(&mut context.pool(), &form).await?;
build_post_response( let person_id = local_user_view.person.id;
context, ActivityChannel::submit_activity(
orig_post.community_id, SendActivityData::FeaturePost(post, local_user_view.person, data.featured),
local_user_view.person.id, &context,
post_id,
) )
.await .await?;
}
build_post_response(&context, orig_post.community_id, person_id, post_id).await
} }

View File

@ -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,21 +15,21 @@ 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)]
impl Perform for CreatePostLike {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn like_post(
let data: &CreatePostLike = self; data: Json<CreatePostLike>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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?; let local_site = LocalSite::read(&mut context.pool()).await?;
// Don't do a downvote if site has downvotes disabled // Don't do a downvote if site has downvotes disabled
@ -68,12 +69,22 @@ impl Perform for CreatePostLike {
// Mark the post as read // Mark the post as read
mark_post_as_read(person_id, post_id, &mut context.pool()).await?; 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( build_post_response(
context, context.deref(),
post.community_id, post.community_id,
local_user_view.person.id, local_user_view.person.id,
post_id, post_id,
) )
.await .await
} }
}

View File

@ -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,14 +21,12 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[async_trait::async_trait(?Send)]
impl Perform for LockPost {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn lock_post(
let data: &LockPost = self; data: Json<LockPost>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?; let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -51,7 +50,7 @@ impl Perform for LockPost {
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
let locked = data.locked; let locked = data.locked;
Post::update( let post = Post::update(
&mut context.pool(), &mut context.pool(),
post_id, post_id,
&PostUpdateForm::builder().locked(Some(locked)).build(), &PostUpdateForm::builder().locked(Some(locked)).build(),
@ -66,12 +65,12 @@ impl Perform for LockPost {
}; };
ModLockPost::create(&mut context.pool(), &form).await?; ModLockPost::create(&mut context.pool(), &form).await?;
build_post_response( let person_id = local_user_view.person.id;
context, ActivityChannel::submit_activity(
orig_post.community_id, SendActivityData::LockPost(post, local_user_view.person, data.locked),
local_user_view.person.id, &context,
post_id,
) )
.await .await?;
}
build_post_response(&context, orig_post.community_id, person_id, post_id).await
} }

View File

@ -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;

View File

@ -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,17 +23,15 @@ 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)]
impl Perform for CreatePostReport {
type Response = PostReportResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostReportResponse, LemmyError> { pub async fn create_post_report(
let data: &CreatePostReport = self; data: Json<CreatePostReport>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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?; let local_site = LocalSite::read(&mut context.pool()).await?;
let reason = sanitize_html(self.reason.trim()); let reason = sanitize_html(data.reason.trim());
check_report_reason(&reason, &local_site)?; check_report_reason(&reason, &local_site)?;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
@ -66,6 +66,16 @@ impl Perform for CreatePostReport {
.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 }))
} }

View File

@ -1,3 +1,3 @@
mod create; pub mod create;
mod list; pub mod list;
mod resolve; pub mod resolve;

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -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(),

View File

@ -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,16 +16,13 @@ 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)]
impl PerformCrud for DeleteComment {
type Response = CommentResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { pub async fn delete_comment(
let data: &DeleteComment = self; data: Json<DeleteComment>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
@ -64,17 +62,29 @@ impl PerformCrud for DeleteComment {
&local_user_view.person, &local_user_view.person,
&post, &post,
false, false,
context, &context,
)
.await?;
let updated_comment_id = updated_comment.id;
ActivityChannel::submit_activity(
SendActivityData::DeleteComment(
updated_comment,
local_user_view.person.clone(),
orig_comment.community,
),
&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?,
} ))
} }

View File

@ -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?,
)) ))
} }

View File

@ -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,16 +17,13 @@ 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)]
impl PerformCrud for RemoveComment {
type Response = CommentResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { pub async fn remove_comment(
let data: &RemoveComment = self; data: Json<RemoveComment>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<CommentResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?; let orig_comment = CommentView::read(&mut context.pool(), comment_id, None).await?;
@ -72,17 +70,30 @@ impl PerformCrud for RemoveComment {
&local_user_view.person.clone(), &local_user_view.person.clone(),
&post, &post,
false, false,
context, &context,
)
.await?;
let updated_comment_id = updated_comment.id;
ActivityChannel::submit_activity(
SendActivityData::RemoveComment(
updated_comment,
local_user_view.person.clone(),
orig_comment.community,
data.reason.clone(),
),
&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?,
} ))
} }

View File

@ -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,16 +30,13 @@ use lemmy_utils::{
validation::is_valid_body_field, validation::is_valid_body_field,
}, },
}; };
use std::ops::Deref;
#[async_trait::async_trait(?Send)]
impl PerformCrud for EditComment {
type Response = CommentResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommentResponse, LemmyError> { pub async fn update_comment(
let data: &EditComment = self; data: Json<EditComment>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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?; let local_site = LocalSite::read(&mut context.pool()).await?;
let comment_id = data.comment_id; let comment_id = data.comment_id;
@ -56,7 +54,7 @@ impl PerformCrud for EditComment {
return Err(LemmyErrorType::NoCommentEditAllowed)?; return Err(LemmyErrorType::NoCommentEditAllowed)?;
} }
let language_id = self.language_id; let language_id = data.language_id;
CommunityLanguage::is_allowed_community_language( CommunityLanguage::is_allowed_community_language(
&mut context.pool(), &mut context.pool(),
language_id, language_id,
@ -91,17 +89,24 @@ impl PerformCrud for EditComment {
&local_user_view.person, &local_user_view.person,
&orig_comment.post, &orig_comment.post,
false, false,
context, &context,
) )
.await?; .await?;
ActivityChannel::submit_activity(
SendActivityData::UpdateComment(updated_comment.clone()),
&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?,
} ))
} }

View File

@ -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,14 +41,12 @@ use lemmy_utils::{
}, },
}; };
#[async_trait::async_trait(?Send)]
impl PerformCrud for CreateCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn create_community(
let data: &CreateCommunity = self; data: Json<CreateCommunity>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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 site_view = SiteView::read_local(&mut context.pool()).await?;
let local_site = site_view.local_site; let local_site = site_view.local_site;
@ -143,6 +140,5 @@ impl PerformCrud for CreateCommunity {
CommunityLanguage::update(&mut context.pool(), languages, community_id).await?; CommunityLanguage::update(&mut context.pool(), languages, community_id).await?;
} }
build_community_response(context, local_user_view, community_id).await build_community_response(&context, local_user_view, community_id).await
}
} }

View File

@ -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,14 +14,12 @@ 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)]
impl PerformCrud for DeleteCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn delete_community(
let data: &DeleteCommunity = self; data: Json<DeleteCommunity>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<CommunityResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
// Fetch the community mods // Fetch the community mods
let community_id = data.community_id; let community_id = data.community_id;
@ -33,7 +32,7 @@ impl PerformCrud for DeleteCommunity {
// Do the delete // Do the delete
let community_id = data.community_id; let community_id = data.community_id;
let deleted = data.deleted; let deleted = data.deleted;
Community::update( let community = Community::update(
&mut context.pool(), &mut context.pool(),
community_id, community_id,
&CommunityUpdateForm::builder() &CommunityUpdateForm::builder()
@ -43,6 +42,11 @@ impl PerformCrud for DeleteCommunity {
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
build_community_response(context, local_user_view, community_id).await ActivityChannel::submit_activity(
} SendActivityData::DeleteCommunity(local_user_view.person.clone(), community, data.deleted),
&context,
)
.await?;
build_community_response(&context, local_user_view, community_id).await
} }

View File

@ -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,14 +19,12 @@ use lemmy_utils::{
utils::time::naive_from_unix, utils::time::naive_from_unix,
}; };
#[async_trait::async_trait(?Send)]
impl PerformCrud for RemoveCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn remove_community(
let data: &RemoveCommunity = self; data: Json<RemoveCommunity>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<CommunityResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
// Verify its an admin (only an admin can remove a community) // Verify its an admin (only an admin can remove a community)
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
@ -33,7 +32,7 @@ impl PerformCrud for RemoveCommunity {
// Do the remove // Do the remove
let community_id = data.community_id; let community_id = data.community_id;
let removed = data.removed; let removed = data.removed;
Community::update( let community = Community::update(
&mut context.pool(), &mut context.pool(),
community_id, community_id,
&CommunityUpdateForm::builder() &CommunityUpdateForm::builder()
@ -54,6 +53,16 @@ impl PerformCrud for RemoveCommunity {
}; };
ModRemoveCommunity::create(&mut context.pool(), &form).await?; ModRemoveCommunity::create(&mut context.pool(), &form).await?;
build_community_response(context, local_user_view, community_id).await ActivityChannel::submit_activity(
} SendActivityData::RemoveCommunity(
local_user_view.person.clone(),
community,
data.reason.clone(),
data.removed,
),
&context,
)
.await?;
build_community_response(&context, local_user_view, community_id).await
} }

View File

@ -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,14 +23,12 @@ 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)]
impl PerformCrud for EditCommunity {
type Response = CommunityResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CommunityResponse, LemmyError> { pub async fn update_community(
let data: &EditCommunity = self; data: Json<EditCommunity>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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?; let local_site = LocalSite::read(&mut context.pool()).await?;
let slur_regex = local_site_to_slur_regex(&local_site); let slur_regex = local_site_to_slur_regex(&local_site);
@ -77,10 +76,15 @@ impl PerformCrud for EditCommunity {
.build(); .build();
let community_id = data.community_id; let community_id = data.community_id;
Community::update(&mut context.pool(), community_id, &community_form) let community = Community::update(&mut context.pool(), community_id, &community_form)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?; .with_lemmy_type(LemmyErrorType::CouldntUpdateCommunity)?;
build_community_response(context, local_user_view, community_id).await ActivityChannel::submit_activity(
} SendActivityData::UpdateCommunity(local_user_view.person.clone(), community),
&context,
)
.await?;
build_community_response(&context, local_user_view, community_id).await
} }

View File

@ -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,14 +13,12 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<CustomEmojiResponse>, LemmyError> {
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CustomEmojiResponse, LemmyError> { let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let data: &CreateCustomEmoji = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = LocalSite::read(&mut context.pool()).await?;
// Make sure user is an admin // Make sure user is an admin
@ -48,6 +46,5 @@ impl PerformCrud for CreateCustomEmoji {
} }
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
Ok(CustomEmojiResponse { custom_emoji: view }) Ok(Json(CustomEmojiResponse { custom_emoji: view }))
}
} }

View File

@ -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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<DeleteCustomEmojiResponse>, LemmyError> {
async fn perform( let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
&self,
context: &Data<LemmyContext>,
) -> Result<DeleteCustomEmojiResponse, LemmyError> {
let data: &DeleteCustomEmoji = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
CustomEmoji::delete(&mut context.pool(), data.id).await?; CustomEmoji::delete(&mut context.pool(), data.id).await?;
Ok(DeleteCustomEmojiResponse { Ok(Json(DeleteCustomEmojiResponse {
id: data.id, id: data.id,
success: true, success: true,
}) }))
}
} }

View File

@ -1,3 +1,3 @@
mod create; pub mod create;
mod delete; pub mod delete;
mod update; pub mod update;

View File

@ -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,14 +13,12 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<CustomEmojiResponse>, LemmyError> {
async fn perform(&self, context: &Data<LemmyContext>) -> Result<CustomEmojiResponse, LemmyError> { let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let data: &EditCustomEmoji = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
let local_site = LocalSite::read(&mut context.pool()).await?; let local_site = LocalSite::read(&mut context.pool()).await?;
// Make sure user is an admin // Make sure user is an admin
@ -47,6 +45,5 @@ impl PerformCrud for EditCustomEmoji {
} }
CustomEmojiKeyword::create(&mut context.pool(), keywords).await?; CustomEmojiKeyword::create(&mut context.pool(), keywords).await?;
let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?; let view = CustomEmojiView::get(&mut context.pool(), emoji.id).await?;
Ok(CustomEmojiResponse { custom_emoji: view }) Ok(Json(CustomEmojiResponse { custom_emoji: view }))
}
} }

View File

@ -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>;
}

View File

@ -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?,
))
} }

View File

@ -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,14 +13,12 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::error::{LemmyError, LemmyErrorType}; use lemmy_utils::error::{LemmyError, LemmyErrorType};
#[async_trait::async_trait(?Send)]
impl PerformCrud for DeletePost {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn delete_post(
let data: &DeletePost = self; data: Json<DeletePost>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?; let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -43,21 +42,21 @@ impl PerformCrud for DeletePost {
} }
// Update the post // Update the post
let post_id = data.post_id; let post = Post::update(
let deleted = data.deleted;
Post::update(
&mut context.pool(), &mut context.pool(),
post_id, data.post_id,
&PostUpdateForm::builder().deleted(Some(deleted)).build(), &PostUpdateForm::builder()
.deleted(Some(data.deleted))
.build(),
) )
.await?; .await?;
build_post_response( let person_id = local_user_view.person.id;
context, ActivityChannel::submit_activity(
orig_post.community_id, SendActivityData::DeletePost(post, local_user_view.person, data.0.clone()),
local_user_view.person.id, &context,
post_id,
) )
.await .await?;
}
build_post_response(&context, orig_post.community_id, person_id, data.post_id).await
} }

View File

@ -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,14 +16,12 @@ use lemmy_db_schema::{
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
#[async_trait::async_trait(?Send)]
impl PerformCrud for RemovePost {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn remove_post(
let data: &RemovePost = self; data: Json<RemovePost>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; context: Data<LemmyContext>,
) -> Result<Json<PostResponse>, LemmyError> {
let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
let post_id = data.post_id; let post_id = data.post_id;
let orig_post = Post::read(&mut context.pool(), post_id).await?; let orig_post = Post::read(&mut context.pool(), post_id).await?;
@ -45,7 +44,7 @@ impl PerformCrud for RemovePost {
// Update the post // Update the post
let post_id = data.post_id; let post_id = data.post_id;
let removed = data.removed; let removed = data.removed;
Post::update( let post = Post::update(
&mut context.pool(), &mut context.pool(),
post_id, post_id,
&PostUpdateForm::builder().removed(Some(removed)).build(), &PostUpdateForm::builder().removed(Some(removed)).build(),
@ -61,12 +60,12 @@ impl PerformCrud for RemovePost {
}; };
ModRemovePost::create(&mut context.pool(), &form).await?; ModRemovePost::create(&mut context.pool(), &form).await?;
build_post_response( let person_id = local_user_view.person.id;
context, ActivityChannel::submit_activity(
orig_post.community_id, SendActivityData::RemovePost(post, local_user_view.person, data.0),
local_user_view.person.id, &context,
post_id,
) )
.await .await?;
}
build_post_response(&context, orig_post.community_id, person_id, post_id).await
} }

View File

@ -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,15 +29,14 @@ 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)]
impl PerformCrud for EditPost {
type Response = PostResponse;
#[tracing::instrument(skip(context))] #[tracing::instrument(skip(context))]
async fn perform(&self, context: &Data<LemmyContext>) -> Result<PostResponse, LemmyError> { pub async fn update_post(
let data: &EditPost = self; data: Json<EditPost>,
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?; 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?; let local_site = LocalSite::read(&mut context.pool()).await?;
let data_url = data.url.as_ref(); let data_url = data.url.as_ref();
@ -85,7 +85,7 @@ impl PerformCrud for EditPost {
let embed_title = embed_title.map(|e| sanitize_html_opt(&e)); let embed_title = embed_title.map(|e| sanitize_html_opt(&e));
let embed_description = embed_description.map(|e| sanitize_html_opt(&e)); let embed_description = embed_description.map(|e| sanitize_html_opt(&e));
let language_id = self.language_id; let language_id = data.language_id;
CommunityLanguage::is_allowed_community_language( CommunityLanguage::is_allowed_community_language(
&mut context.pool(), &mut context.pool(),
language_id, language_id,
@ -107,16 +107,17 @@ impl PerformCrud for EditPost {
.build(); .build();
let post_id = data.post_id; let post_id = data.post_id;
Post::update(&mut context.pool(), post_id, &post_form) let updated_post = Post::update(&mut context.pool(), post_id, &post_form)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?; .with_lemmy_type(LemmyErrorType::CouldntUpdatePost)?;
ActivityChannel::submit_activity(SendActivityData::UpdatePost(updated_post), &context).await?;
build_post_response( build_post_response(
context, context.deref(),
orig_post.community_id, orig_post.community_id,
local_user_view.person.id, local_user_view.person.id,
post_id, post_id,
) )
.await .await
} }
}

View File

@ -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,17 +28,12 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
async fn perform( let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
&self,
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 local_site = LocalSite::read(&mut context.pool()).await?;
let content = sanitize_html(&data.content); let content = sanitize_html(&data.content);
@ -57,8 +53,7 @@ impl PerformCrud for CreatePrivateMessage {
.recipient_id(data.recipient_id) .recipient_id(data.recipient_id)
.build(); .build();
let inserted_private_message = let inserted_private_message = PrivateMessage::create(&mut context.pool(), &private_message_form)
PrivateMessage::create(&mut context.pool(), &private_message_form)
.await .await
.with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?; .with_lemmy_type(LemmyErrorType::CouldntCreatePrivateMessage)?;
@ -97,8 +92,13 @@ impl PerformCrud for CreatePrivateMessage {
.await; .await;
} }
Ok(PrivateMessageResponse { ActivityChannel::submit_activity(
SendActivityData::CreatePrivateMessage(view.clone()),
&context,
)
.await?;
Ok(Json(PrivateMessageResponse {
private_message_view: view, private_message_view: view,
}) }))
}
} }

View File

@ -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,22 +13,16 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
async fn perform( let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
&self,
context: &Data<LemmyContext>,
) -> Result<PrivateMessageResponse, LemmyError> {
let data: &DeletePrivateMessage = self;
let local_user_view = local_user_view_from_jwt(&data.auth, context).await?;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let orig_private_message = let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
PrivateMessage::read(&mut context.pool(), private_message_id).await?;
if local_user_view.person.id != orig_private_message.creator_id { if local_user_view.person.id != orig_private_message.creator_id {
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
} }
@ -35,7 +30,7 @@ impl PerformCrud for DeletePrivateMessage {
// Doing the update // Doing the update
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let deleted = data.deleted; let deleted = data.deleted;
PrivateMessage::update( let private_message = PrivateMessage::update(
&mut context.pool(), &mut context.pool(),
private_message_id, private_message_id,
&PrivateMessageUpdateForm::builder() &PrivateMessageUpdateForm::builder()
@ -45,9 +40,14 @@ impl PerformCrud for DeletePrivateMessage {
.await .await
.with_lemmy_type(LemmyErrorType::CouldntUpdatePrivateMessage)?; .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?; let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
Ok(PrivateMessageResponse { Ok(Json(PrivateMessageResponse {
private_message_view: view, private_message_view: view,
}) }))
}
} }

View File

@ -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,23 +20,17 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<PrivateMessageResponse>, LemmyError> {
async fn perform( let local_user_view = local_user_view_from_jwt(&data.auth, &context).await?;
&self,
context: &Data<LemmyContext>,
) -> Result<PrivateMessageResponse, LemmyError> {
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?; let local_site = LocalSite::read(&mut context.pool()).await?;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
let orig_private_message = let orig_private_message = PrivateMessage::read(&mut context.pool(), private_message_id).await?;
PrivateMessage::read(&mut context.pool(), private_message_id).await?;
if local_user_view.person.id != orig_private_message.creator_id { if local_user_view.person.id != orig_private_message.creator_id {
return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?; return Err(LemmyErrorType::EditPrivateMessageNotAllowed)?;
} }
@ -59,8 +54,13 @@ impl PerformCrud for EditPrivateMessage {
let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?; let view = PrivateMessageView::read(&mut context.pool(), private_message_id).await?;
Ok(PrivateMessageResponse { ActivityChannel::submit_activity(
SendActivityData::UpdatePrivateMessage(view.clone()),
&context,
)
.await?;
Ok(Json(PrivateMessageResponse {
private_message_view: view, private_message_view: view,
}) }))
}
} }

View File

@ -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,14 +37,11 @@ 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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<LoginResponse>, LemmyError> {
async fn perform(&self, context: &Data<LemmyContext>) -> Result<LoginResponse, LemmyError> {
let data: &Register = self;
let site_view = SiteView::read_local(&mut context.pool()).await?; let site_view = SiteView::read_local(&mut context.pool()).await?;
let local_site = site_view.local_site; let local_site = site_view.local_site;
let require_registration_application = let require_registration_application =
@ -209,6 +205,5 @@ impl PerformCrud for Register {
} }
} }
Ok(login_response) Ok(Json(login_response))
}
} }

View File

@ -1,21 +1,20 @@
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>,
#[tracing::instrument(skip(self, context))] ) -> Result<Json<DeleteAccountResponse>, LemmyError> {
async fn perform(&self, context: &Data<LemmyContext>) -> Result<Self::Response, LemmyError> { let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), &context).await?;
let data = self;
let local_user_view = local_user_view_from_jwt(data.auth.as_ref(), context).await?;
// Verify the password // Verify the password
let valid: bool = verify( let valid: bool = verify(
@ -27,6 +26,11 @@ impl PerformCrud for DeleteAccount {
return Err(LemmyErrorType::IncorrectLogin)?; return Err(LemmyErrorType::IncorrectLogin)?;
} }
Ok(DeleteAccountResponse {}) ActivityChannel::submit_activity(
} SendActivityData::DeleteUser(local_user_view.person),
&context,
)
.await?;
Ok(Json(DeleteAccountResponse {}))
} }

View File

@ -1,2 +1,2 @@
mod create; pub mod create;
mod delete; pub mod delete;

View File

@ -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 }

View File

@ -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,40 +130,35 @@ 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,
async fn send_activity( context: Data<LemmyContext>,
request: &Self,
_response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> 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 site = SiteOrCommunity::Site(SiteView::read_local(&mut context.pool()).await?.site.into());
let expires = request.expires.map(naive_from_unix); let expires = data.expires.map(naive_from_unix);
// if the action affects a local user, federate to other instances // if the action affects a local user, federate to other instances
if person.local { if banned_user.local {
if request.ban { if data.ban {
BlockUser::send( BlockUser::send(
&site, &site,
&person.into(), &banned_user.into(),
&local_user_view.person.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(
&site, &site,
&person.into(), &banned_user.into(),
&local_user_view.person.into(), &mod_.into(),
request.reason.clone(), data.reason.clone(),
context, &context,
) )
.await .await
} }
@ -173,46 +166,38 @@ impl SendActivity for BanPerson {
Ok(()) Ok(())
} }
} }
}
#[async_trait::async_trait] pub(crate) async fn send_ban_from_community(
impl SendActivity for BanFromCommunity { mod_: Person,
type Response = BanFromCommunityResponse; community_id: CommunityId,
banned_person: Person,
async fn send_activity( data: BanFromCommunity,
request: &Self, context: Data<LemmyContext>,
_response: &Self::Response, ) -> LemmyResult<()> {
context: &Data<LemmyContext>, let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
) -> 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? .await?
.into(); .into();
let banned_person: ApubPerson = Person::read(&mut context.pool(), request.person_id) let expires = data.expires.map(naive_from_unix);
.await?
.into();
let expires = request.expires.map(naive_from_unix);
if request.ban { if data.ban {
BlockUser::send( BlockUser::send(
&SiteOrCommunity::Community(community), &SiteOrCommunity::Community(community),
&banned_person, &banned_person.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), &SiteOrCommunity::Community(community),
&banned_person, &banned_person.into(),
&local_user_view.person.clone().into(), &mod_.into(),
request.reason.clone(), data.reason.clone(),
context, &context,
) )
.await .await
} }
} }
}

View File

@ -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,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; let actor: ApubPerson = actor.into();
let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id) let community: ApubCommunity = Community::read(&mut context.pool(), community_id)
.await? .await?
.into(); .into();
let updated_mod: ApubPerson = Person::read(&mut context.pool(), request.person_id) let updated_mod: ApubPerson = Person::read(&mut context.pool(), updated_mod_id)
.await? .await?
.into(); .into();
if request.added { if added {
CollectionAdd::send_add_mod( CollectionAdd::send_add_mod(&community, &updated_mod, &actor, &context).await
&community,
&updated_mod,
&local_user_view.person.into(),
context,
)
.await
} else { } else {
CollectionRemove::send_remove_mod( CollectionRemove::send_remove_mod(&community, &updated_mod, &actor, &context).await
&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,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; let actor: ApubPerson = actor.into();
let community = Community::read(&mut context.pool(), response.post_view.community.id) let post: ApubPost = post.into();
let community = Community::read(&mut context.pool(), post.community_id)
.await? .await?
.into(); .into();
let post = response.post_view.post.clone().into(); if featured {
let person = local_user_view.person.into(); CollectionAdd::send_add_featured_post(&community, &post, &actor, &context).await
if request.featured {
CollectionAdd::send_add_featured_post(&community, &post, &person, context).await
} else { } else {
CollectionRemove::send_remove_featured_post(&community, &post, &person, context).await CollectionRemove::send_remove_featured_post(&community, &post, &actor, &context).await
}
} }
} }

View File

@ -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,32 +101,30 @@ 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,
response: &Self::Response,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; let community: ApubCommunity = Community::read(&mut context.pool(), post.community_id)
.await?
.into();
let id = generate_activity_id( let id = generate_activity_id(
LockType::Lock, LockType::Lock,
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
)?; )?;
let community_id = response.post_view.community.actor_id.clone(); let community_id = community.actor_id.inner().clone();
let actor = local_user_view.person.actor_id.clone().into();
let lock = LockPage { let lock = LockPage {
actor, actor: actor.actor_id.clone().into(),
to: vec![public()], to: vec![public()],
object: response.post_view.post.ap_id.clone().into(), object: ObjectId::from(post.ap_id),
cc: vec![community_id.clone().into()], cc: vec![community_id.clone()],
kind: LockType::Lock, kind: LockType::Lock,
id, id,
audience: Some(community_id.into()), audience: Some(community_id.into()),
}; };
let activity = if request.locked { let activity = if locked {
AnnouncableActivities::LockPost(lock) AnnouncableActivities::LockPost(lock)
} else { } else {
let id = generate_activity_id( let id = generate_activity_id(
@ -146,16 +142,14 @@ impl SendActivity for LockPost {
}; };
AnnouncableActivities::UndoLockPost(undo) AnnouncableActivities::UndoLockPost(undo)
}; };
let community = Community::read(&mut context.pool(), response.post_view.community.id).await?;
send_activity_in_community( send_activity_in_community(
activity, activity,
&local_user_view.person.into(), &actor.into(),
&community.into(), &community,
ActivitySendTargets::empty(), ActivitySendTargets::empty(),
true, true,
context, &context,
) )
.await?; .await?;
Ok(()) Ok(())
} }
}

View File

@ -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
} }
} }

View File

@ -10,47 +10,27 @@ 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>,
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
}
}
impl UpdateCommunity {
#[tracing::instrument(skip_all)]
pub async fn send(
community: ApubCommunity,
actor: &ApubPerson,
context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let community: ApubCommunity = community.into();
let actor: ApubPerson = actor.into();
let id = generate_activity_id( let id = generate_activity_id(
UpdateType::Update, UpdateType::Update,
&context.settings().get_protocol_and_hostname(), &context.settings().get_protocol_and_hostname(),
@ -58,7 +38,7 @@ impl UpdateCommunity {
let update = UpdateCommunity { let update = UpdateCommunity {
actor: actor.id().into(), actor: actor.id().into(),
to: vec![public()], to: vec![public()],
object: Box::new(community.clone().into_json(context).await?), object: Box::new(community.clone().into_json(&context).await?),
cc: vec![community.id()], cc: vec![community.id()],
kind: UpdateType::Update, kind: UpdateType::Update,
id: id.clone(), id: id.clone(),
@ -68,15 +48,14 @@ impl UpdateCommunity {
let activity = AnnouncableActivities::UpdateCommunity(update); let activity = AnnouncableActivities::UpdateCommunity(update);
send_activity_in_community( send_activity_in_community(
activity, activity,
actor, &actor,
&community, &community,
ActivitySendTargets::empty(), ActivitySendTargets::empty(),
true, true,
context, &context,
) )
.await .await
} }
}
#[async_trait::async_trait] #[async_trait::async_trait]
impl ActivityHandler for UpdateCommunity { impl ActivityHandler for UpdateCommunity {
@ -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
}
}

View File

@ -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(

View File

@ -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,

View File

@ -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,59 +21,17 @@ 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;
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::Create,
context,
)
.await
}
}
#[async_trait::async_trait]
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, kind: CreateOrUpdateType,
context: &Data<LemmyContext>, context: Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let recipient_id = private_message.recipient_id; let actor: ApubPerson = pm_view.creator.into();
let sender: ApubPerson = Person::read(&mut context.pool(), sender_id).await?.into(); let recipient: ApubPerson = pm_view.recipient.into();
let recipient: ApubPerson = Person::read(&mut context.pool(), recipient_id)
.await?
.into();
let id = generate_activity_id( let id = generate_activity_id(
kind.clone(), kind.clone(),
@ -82,17 +39,15 @@ impl CreateOrUpdateChatMessage {
)?; )?;
let create_or_update = CreateOrUpdateChatMessage { let create_or_update = CreateOrUpdateChatMessage {
id: id.clone(), id: id.clone(),
actor: sender.id().into(), actor: actor.id().into(),
to: [recipient.id().into()], to: [recipient.id().into()],
object: ApubPrivateMessage(private_message.clone()) object: ApubPrivateMessage(pm_view.private_message.clone())
.into_json(context) .into_json(&context)
.await?, .await?,
kind, kind,
}; };
let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox()); let inbox = ActivitySendTargets::to_inbox(recipient.shared_inbox_or_inbox());
send_lemmy_activity(&context, create_or_update, &actor, inbox, true).await
send_lemmy_activity(context, create_or_update, &sender, inbox, true).await
}
} }
#[async_trait::async_trait] #[async_trait::async_trait]

View File

@ -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,20 +16,13 @@ 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;
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 actor: ApubPerson = local_user_view.person.into();
delete_user_account( delete_user_account(
actor.id, actor.id,
&mut context.pool(), &mut context.pool(),
@ -55,7 +47,7 @@ impl SendActivity for DeleteAccount {
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(())
} }
} }

View File

@ -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(())
} }

View File

@ -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
}
}

View File

@ -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(
request: &Self,
_response: &Self::Response,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let local_user_view = local_user_view_from_jwt(&request.auth, context).await?; let community: ApubCommunity = community.into();
let person = local_user_view.person.clone().into(); let actor: ApubPerson = person.into();
let community: ApubCommunity = Community::read(&mut context.pool(), request.community_id) if follow {
.await? Follow::send(&actor, &community, context).await
.into();
if community.local {
Ok(())
} else if request.follow {
Follow::send(&person, &community, context).await
} else { } else {
UndoFollow::send(&person, &community, context).await UndoFollow::send(&actor, &community, context).await
}
} }
} }

View File

@ -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 {

View File

@ -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
} }
} }

View File

@ -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 }

View File

@ -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> {

View File

@ -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<'_>,

View File

@ -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 {

View File

@ -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();

View File

@ -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;

View File

@ -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,
}; };

View File

@ -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)

View File

@ -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<'_>,

View File

@ -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?;

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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,
} }

View File

@ -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,
} }

View File

@ -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
} }
} }

View File

@ -308,8 +308,11 @@ 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)
{ {
// 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()); query = query.filter(post_read::post_id.is_null());
} }
}
if options.local_user.is_some() { if options.local_user.is_some() {
// Filter out the rows with missing languages // Filter out the rows with missing languages

View File

@ -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 {

View File

@ -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_manage_updated_at (_tbl regclass);
DROP FUNCTION IF EXISTS diesel_set_updated_at (); DROP FUNCTION IF EXISTS diesel_set_updated_at ();

View File

@ -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;

View File

@ -1,2 +1,4 @@
drop table user_ban; DROP TABLE user_ban;
drop table user_;
DROP TABLE user_;

View File

@ -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');

View File

@ -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;

View File

@ -1,10 +1,10 @@
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'),
@ -31,49 +31,51 @@ insert into category (name) values
('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
); );

View File

@ -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;

View File

@ -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)
); );

View File

@ -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;

View File

@ -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)
); );

View File

@ -1,2 +1,4 @@
drop view post_view; DROP VIEW post_view;
drop function hot_rank;
DROP FUNCTION hot_rank;

View File

@ -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 name from community where p.community_id = community.id) as community_name, SELECT
(select removed from community c where p.community_id = c.id) as community_removed, name
(select count(*) from comment where comment.post_id = p.id) as number_of_comments, FROM
coalesce(sum(pl.score), 0) as score, user_
count (case when pl.score = 1 then 1 else null end) as upvotes, WHERE
count (case when pl.score = -1 then 1 else null end) as downvotes, p.creator_id = user_.id) AS creator_name,
hot_rank(coalesce(sum(pl.score) , 0), p.published) as hot_rank (
from post p SELECT
left join post_like pl on p.id = pl.post_id name
group by p.id FROM
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
select
ap.*, ap.*,
u.id as user_id, u.id AS user_id,
coalesce(pl.score, 0) as my_vote, 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
(select ps.id::bool from post_saved ps where u.id = ps.user_id and ps.post_id = ap.id) as saved cf.id::bool
from user_ u FROM
cross join all_post ap community_follower cf
left join post_like pl on u.id = pl.user_id and ap.id = pl.post_id WHERE
u.id = cf.user_id
union all AND cf.community_id = ap.community_id) AS subscribed,
(
select 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.*, ap.*,
null as user_id, NULL AS user_id,
null as my_vote, NULL AS my_vote,
null as subscribed, NULL AS subscribed,
null as read, NULL AS read,
null as saved NULL AS saved
from all_post ap FROM
; all_post ap;

View File

@ -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;

View File

@ -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
(select name from user_ u where c.creator_id = u.id) as creator_name, name
(select name from category ct where c.category_id = ct.id) as category_name, FROM
(select count(*) from community_follower cf where cf.community_id = c.id) as number_of_subscribers, user_ u
(select count(*) from post p where p.community_id = c.id) as number_of_posts, WHERE
(select count(*) from comment co, post p where c.id = p.community_id and p.id = co.post_id) as number_of_comments c.creator_id = u.id) AS creator_name,
from community c (
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
select
ac.*, ac.*,
u.id as user_id, 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 cf.id::boolean
FROM
union all community_follower cf
WHERE
select 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.*, ac.*,
null as user_id, NULL AS user_id,
null as subscribed NULL AS subscribed
from all_community ac FROM
; all_community ac;
create view community_moderator_view as CREATE VIEW community_moderator_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
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;
create view community_follower_view as CREATE VIEW community_follower_view AS
select *, 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
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 CREATE VIEW community_user_ban_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_user_ban cm; 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;
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;

View File

@ -1,2 +1,4 @@
drop view reply_view; DROP VIEW reply_view;
drop view comment_view;
DROP VIEW comment_view;

View File

@ -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 u.banned from user_ u where c.creator_id = u.id) as banned, SELECT
(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, community_id
(select name from user_ where c.creator_id = user_.id) as creator_name, FROM
coalesce(sum(cl.score), 0) as score, post p
count (case when cl.score = 1 then 1 else null end) as upvotes, WHERE
count (case when cl.score = -1 then 1 else null end) as downvotes p.id = c.post_id),
from comment c (
left join comment_like cl on c.id = cl.comment_id SELECT
group by c.id u.banned
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
select
ac.*, ac.*,
u.id as user_id, u.id AS user_id,
coalesce(cl.score, 0) as my_vote, 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 SELECT
cross join all_comment ac cs.id::bool
left join comment_like cl on u.id = cl.user_id and ac.id = cl.comment_id FROM
comment_saved cs
union all WHERE
u.id = cs.user_id
select 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.*, ac.*,
null as user_id, NULL AS user_id,
null as my_vote, NULL AS my_vote,
null as saved NULL AS saved
from all_comment ac FROM
; all_comment ac;
create view reply_view as CREATE VIEW reply_view AS
with closereply as ( with closereply AS (
select SELECT
c2.id, c2.id,
c2.creator_id as sender_id, c2.creator_id AS sender_id,
c.creator_id as recipient_id c.creator_id AS recipient_id
from comment c FROM
inner join comment c2 on c.id = c2.parent_id comment c
where c2.creator_id != c.creator_id INNER JOIN comment c2 ON c.id = c2.parent_id
WHERE
c2.creator_id != c.creator_id
-- Do union where post is null -- Do union where post is null
union UNION
select SELECT
c.id, c.id,
c.creator_id as sender_id, c.creator_id AS sender_id,
p.creator_id as recipient_id p.creator_id AS recipient_id
from comment c, post p FROM
where c.post_id = p.id and c.parent_id is null and c.creator_id != p.creator_id 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
cv.*,
closereply.recipient_id closereply.recipient_id
from comment_view cv, closereply FROM
where closereply.id = cv.id comment_view cv,
; closereply
WHERE
closereply.id = cv.id;

View File

@ -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