Moving settings to Database. (#2492)
* Moving settings to Database. - Moves many settings into the database. Fixes #2285 - Adds a local_site and instance table. Fixes #2365 . Fixes #2368 - Separates SQL update an insert forms, to avoid runtime errors. - Adds TypedBuilder to all the SQL forms, instead of default. * Fix weird clippy issue. * Removing extra lines. * Some fixes from suggestions. * Fixing apub tests. * Using instance creation helper function. * Move forms to their own line. * Trying to fix local_site_data, still broken. * Fixing federation tests. * Trying to fix check features 1. * Addressing PR comments. * Adding check_apub to all verify functions.
This commit is contained in:
parent
276a8c2bd3
commit
235cc8b228
179 changed files with 4602 additions and 4013 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -2005,6 +2005,7 @@ dependencies = [
|
|||
"lemmy_db_views_moderator",
|
||||
"lemmy_utils",
|
||||
"percent-encoding",
|
||||
"regex",
|
||||
"reqwest",
|
||||
"reqwest-middleware",
|
||||
"rosetta-i18n",
|
||||
|
@ -2096,6 +2097,7 @@ dependencies = [
|
|||
"sha2",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"typed-builder",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
@ -2110,6 +2112,7 @@ dependencies = [
|
|||
"serial_test",
|
||||
"tracing",
|
||||
"typed-builder",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -2227,6 +2230,7 @@ dependencies = [
|
|||
"strum_macros",
|
||||
"tracing",
|
||||
"tracing-error",
|
||||
"typed-builder",
|
||||
"url",
|
||||
"uuid 1.1.2",
|
||||
]
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
"browser": true
|
||||
},
|
||||
"plugins": [
|
||||
"jane"
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"plugin:jane/recommended",
|
||||
"plugin:jane/typescript"
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
module.exports = Object.assign(require('eslint-plugin-jane/prettier-ts'), {
|
||||
arrowParens: 'avoid',
|
||||
module.exports = Object.assign(require("eslint-plugin-prettier"), {
|
||||
arrowParens: "avoid",
|
||||
semi: true,
|
||||
});
|
||||
|
|
|
@ -14,15 +14,17 @@
|
|||
"devDependencies": {
|
||||
"@sniptt/monads": "^0.5.10",
|
||||
"@types/jest": "^26.0.23",
|
||||
"@typescript-eslint/eslint-plugin": "^5.21.0",
|
||||
"@typescript-eslint/parser": "^5.21.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"eslint": "^8.20.0",
|
||||
"eslint-plugin-jane": "^11.2.2",
|
||||
"eslint": "^8.25.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "^27.0.6",
|
||||
"lemmy-js-client": "0.17.0-rc.37",
|
||||
"lemmy-js-client": "0.17.0-rc.47",
|
||||
"node-fetch": "^2.6.1",
|
||||
"prettier": "^2.7.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"ts-jest": "^27.0.3",
|
||||
"typescript": "^4.6.4"
|
||||
"typescript": "^4.8.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
jest.setTimeout(180000);
|
||||
import {None, Some} from '@sniptt/monads';
|
||||
import { CommentView } from 'lemmy-js-client';
|
||||
import { PostResponse } from 'lemmy-js-client';
|
||||
import { None, Some } from "@sniptt/monads";
|
||||
import { CommentView } from "lemmy-js-client";
|
||||
import { PostResponse } from "lemmy-js-client";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
|
@ -31,7 +31,7 @@ import {
|
|||
getComments,
|
||||
getCommentParentId,
|
||||
resolveCommunity,
|
||||
} from './shared';
|
||||
} from "./shared";
|
||||
|
||||
let postRes: PostResponse;
|
||||
|
||||
|
@ -41,10 +41,7 @@ beforeAll(async () => {
|
|||
await followBeta(alpha);
|
||||
await followBeta(gamma);
|
||||
let betaCommunity = (await resolveBetaCommunity(alpha)).community;
|
||||
postRes = await createPost(
|
||||
alpha,
|
||||
betaCommunity.unwrap().community.id
|
||||
);
|
||||
postRes = await createPost(alpha, betaCommunity.unwrap().community.id);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
|
@ -65,7 +62,7 @@ function assertCommentFederation(
|
|||
expect(commentOne.comment.removed).toBe(commentOne.comment.removed);
|
||||
}
|
||||
|
||||
test('Create a comment', async () => {
|
||||
test("Create a comment", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
expect(commentRes.comment_view.comment.content).toBeDefined();
|
||||
expect(commentRes.comment_view.community.local).toBe(false);
|
||||
|
@ -73,7 +70,9 @@ test('Create a comment', async () => {
|
|||
expect(commentRes.comment_view.counts.score).toBe(1);
|
||||
|
||||
// Make sure that comment is liked on beta
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
expect(betaComment).toBeDefined();
|
||||
expect(betaComment.community.local).toBe(true);
|
||||
expect(betaComment.creator.local).toBe(false);
|
||||
|
@ -81,15 +80,17 @@ test('Create a comment', async () => {
|
|||
assertCommentFederation(betaComment, commentRes.comment_view);
|
||||
});
|
||||
|
||||
test('Create a comment in a non-existent post', async () => {
|
||||
let commentRes = await createComment(alpha, -1, None) as any;
|
||||
expect(commentRes.error).toBe('couldnt_find_post');
|
||||
test("Create a comment in a non-existent post", async () => {
|
||||
let commentRes = (await createComment(alpha, -1, None)) as any;
|
||||
expect(commentRes.error).toBe("couldnt_find_post");
|
||||
});
|
||||
|
||||
test('Update a comment', async () => {
|
||||
test("Update a comment", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
// Federate the comment first
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment;
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment;
|
||||
assertCommentFederation(betaComment.unwrap(), commentRes.comment_view);
|
||||
|
||||
let updateCommentRes = await editComment(
|
||||
|
@ -97,23 +98,19 @@ test('Update a comment', async () => {
|
|||
commentRes.comment_view.comment.id
|
||||
);
|
||||
expect(updateCommentRes.comment_view.comment.content).toBe(
|
||||
'A jest test federated comment update'
|
||||
"A jest test federated comment update"
|
||||
);
|
||||
expect(updateCommentRes.comment_view.community.local).toBe(false);
|
||||
expect(updateCommentRes.comment_view.creator.local).toBe(true);
|
||||
|
||||
// Make sure that post is updated on beta
|
||||
let betaCommentUpdated = (await resolveComment(
|
||||
beta,
|
||||
commentRes.comment_view.comment
|
||||
)).comment.unwrap();
|
||||
assertCommentFederation(
|
||||
betaCommentUpdated,
|
||||
updateCommentRes.comment_view
|
||||
);
|
||||
let betaCommentUpdated = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
assertCommentFederation(betaCommentUpdated, updateCommentRes.comment_view);
|
||||
});
|
||||
|
||||
test('Delete a comment', async () => {
|
||||
test("Delete a comment", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
|
||||
let deleteCommentRes = await deleteComment(
|
||||
|
@ -125,8 +122,11 @@ test('Delete a comment', async () => {
|
|||
expect(deleteCommentRes.comment_view.comment.content).toBe("");
|
||||
|
||||
// Make sure that comment is undefined on beta
|
||||
let betaCommentRes = await resolveComment(beta, commentRes.comment_view.comment) as any;
|
||||
expect(betaCommentRes.error).toBe('couldnt_find_object');
|
||||
let betaCommentRes = (await resolveComment(
|
||||
beta,
|
||||
commentRes.comment_view.comment
|
||||
)) as any;
|
||||
expect(betaCommentRes.error).toBe("couldnt_find_object");
|
||||
|
||||
let undeleteCommentRes = await deleteComment(
|
||||
alpha,
|
||||
|
@ -136,15 +136,14 @@ test('Delete a comment', async () => {
|
|||
expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false);
|
||||
|
||||
// Make sure that comment is undeleted on beta
|
||||
let betaComment2 = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment2 = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
expect(betaComment2.comment.deleted).toBe(false);
|
||||
assertCommentFederation(
|
||||
betaComment2,
|
||||
undeleteCommentRes.comment_view
|
||||
);
|
||||
assertCommentFederation(betaComment2, undeleteCommentRes.comment_view);
|
||||
});
|
||||
|
||||
test('Remove a comment from admin and community on the same instance', async () => {
|
||||
test("Remove a comment from admin and community on the same instance", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
|
||||
// Get the id for beta
|
||||
|
@ -158,14 +157,20 @@ test('Remove a comment from admin and community on the same instance', async ()
|
|||
expect(removeCommentRes.comment_view.comment.content).toBe("");
|
||||
|
||||
// Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it)
|
||||
let refetchedPostComments = await getComments(alpha, postRes.post_view.post.id);
|
||||
let refetchedPostComments = await getComments(
|
||||
alpha,
|
||||
postRes.post_view.post.id
|
||||
);
|
||||
expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
|
||||
|
||||
let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
|
||||
expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
|
||||
|
||||
// Make sure that comment is unremoved on beta
|
||||
let refetchedPostComments2 = await getComments(alpha, postRes.post_view.post.id);
|
||||
let refetchedPostComments2 = await getComments(
|
||||
alpha,
|
||||
postRes.post_view.post.id
|
||||
);
|
||||
expect(refetchedPostComments2.comments[0].comment.removed).toBe(false);
|
||||
assertCommentFederation(
|
||||
refetchedPostComments2.comments[0],
|
||||
|
@ -173,7 +178,7 @@ test('Remove a comment from admin and community on the same instance', async ()
|
|||
);
|
||||
});
|
||||
|
||||
test('Remove a comment from admin and community on different instance', async () => {
|
||||
test("Remove a comment from admin and community on different instance", async () => {
|
||||
let alpha_user = await registerUser(alpha);
|
||||
let newAlphaApi: API = {
|
||||
client: alpha.client,
|
||||
|
@ -186,11 +191,17 @@ test('Remove a comment from admin and community on different instance', async ()
|
|||
newAlphaApi,
|
||||
newCommunity.community_view.community.id
|
||||
);
|
||||
let commentRes = await createComment(newAlphaApi, newPost.post_view.post.id, None);
|
||||
let commentRes = await createComment(
|
||||
newAlphaApi,
|
||||
newPost.post_view.post.id,
|
||||
None
|
||||
);
|
||||
expect(commentRes.comment_view.comment.content).toBeDefined();
|
||||
|
||||
// Beta searches that to cache it, then removes it
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
let removeCommentRes = await removeComment(
|
||||
beta,
|
||||
true,
|
||||
|
@ -199,29 +210,39 @@ test('Remove a comment from admin and community on different instance', async ()
|
|||
expect(removeCommentRes.comment_view.comment.removed).toBe(true);
|
||||
|
||||
// Make sure its not removed on alpha
|
||||
let refetchedPostComments = await getComments(alpha, newPost.post_view.post.id);
|
||||
let refetchedPostComments = await getComments(
|
||||
alpha,
|
||||
newPost.post_view.post.id
|
||||
);
|
||||
expect(refetchedPostComments.comments[0].comment.removed).toBe(false);
|
||||
assertCommentFederation(refetchedPostComments.comments[0], commentRes.comment_view);
|
||||
assertCommentFederation(
|
||||
refetchedPostComments.comments[0],
|
||||
commentRes.comment_view
|
||||
);
|
||||
});
|
||||
|
||||
test('Unlike a comment', async () => {
|
||||
test("Unlike a comment", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
|
||||
expect(unlike.comment_view.counts.score).toBe(0);
|
||||
|
||||
// Make sure that post is unliked on beta
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
expect(betaComment).toBeDefined();
|
||||
expect(betaComment.community.local).toBe(true);
|
||||
expect(betaComment.creator.local).toBe(false);
|
||||
expect(betaComment.counts.score).toBe(0);
|
||||
});
|
||||
|
||||
test('Federated comment like', async () => {
|
||||
test("Federated comment like", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
|
||||
// Find the comment on beta
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
|
||||
let like = await likeComment(beta, 1, betaComment.comment);
|
||||
expect(like.comment_view.counts.score).toBe(2);
|
||||
|
@ -231,10 +252,12 @@ test('Federated comment like', async () => {
|
|||
expect(postComments.comments[0].counts.score).toBe(2);
|
||||
});
|
||||
|
||||
test('Reply to a comment', async () => {
|
||||
test("Reply to a comment", async () => {
|
||||
// Create a comment on alpha, find it on beta
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
|
||||
// find that comment id on beta
|
||||
|
||||
|
@ -247,7 +270,9 @@ test('Reply to a comment', async () => {
|
|||
expect(replyRes.comment_view.comment.content).toBeDefined();
|
||||
expect(replyRes.comment_view.community.local).toBe(true);
|
||||
expect(replyRes.comment_view.creator.local).toBe(true);
|
||||
expect(getCommentParentId(replyRes.comment_view.comment).unwrap()).toBe(betaComment.comment.id);
|
||||
expect(getCommentParentId(replyRes.comment_view.comment).unwrap()).toBe(
|
||||
betaComment.comment.id
|
||||
);
|
||||
expect(replyRes.comment_view.counts.score).toBe(1);
|
||||
|
||||
// Make sure that comment is seen on alpha
|
||||
|
@ -257,16 +282,18 @@ test('Reply to a comment', async () => {
|
|||
let postComments = await getComments(alpha, postRes.post_view.post.id);
|
||||
let alphaComment = postComments.comments[0];
|
||||
expect(alphaComment.comment.content).toBeDefined();
|
||||
expect(getCommentParentId(alphaComment.comment).unwrap()).toBe(postComments.comments[1].comment.id);
|
||||
expect(getCommentParentId(alphaComment.comment).unwrap()).toBe(
|
||||
postComments.comments[1].comment.id
|
||||
);
|
||||
expect(alphaComment.community.local).toBe(false);
|
||||
expect(alphaComment.creator.local).toBe(false);
|
||||
expect(alphaComment.counts.score).toBe(1);
|
||||
assertCommentFederation(alphaComment, replyRes.comment_view);
|
||||
});
|
||||
|
||||
test('Mention beta', async () => {
|
||||
test("Mention beta", async () => {
|
||||
// Create a mention on alpha
|
||||
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, None);
|
||||
let mentionRes = await createComment(
|
||||
alpha,
|
||||
|
@ -286,23 +313,29 @@ test('Mention beta', async () => {
|
|||
expect(mentionsRes.mentions[0].counts.score).toBe(1);
|
||||
});
|
||||
|
||||
test('Comment Search', async () => {
|
||||
test("Comment Search", async () => {
|
||||
let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
|
||||
let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap();
|
||||
let betaComment = (
|
||||
await resolveComment(beta, commentRes.comment_view.comment)
|
||||
).comment.unwrap();
|
||||
assertCommentFederation(betaComment, commentRes.comment_view);
|
||||
});
|
||||
|
||||
test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => {
|
||||
test("A and G subscribe to B (center) A posts, G mentions B, it gets announced to A", async () => {
|
||||
// Create a local post
|
||||
let alphaCommunity = (await resolveCommunity(alpha, "!main@lemmy-alpha:8541")).community.unwrap();
|
||||
let alphaCommunity = (
|
||||
await resolveCommunity(alpha, "!main@lemmy-alpha:8541")
|
||||
).community.unwrap();
|
||||
let alphaPost = await createPost(alpha, alphaCommunity.community.id);
|
||||
expect(alphaPost.post_view.community.local).toBe(true);
|
||||
|
||||
// Make sure gamma sees it
|
||||
let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post.unwrap();
|
||||
let gammaPost = (
|
||||
await resolvePost(gamma, alphaPost.post_view.post)
|
||||
).post.unwrap();
|
||||
|
||||
let commentContent =
|
||||
'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551';
|
||||
"A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551";
|
||||
let commentRes = await createComment(
|
||||
gamma,
|
||||
gammaPost.post.id,
|
||||
|
@ -315,12 +348,18 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
|||
expect(commentRes.comment_view.counts.score).toBe(1);
|
||||
|
||||
// Make sure alpha sees it
|
||||
let alphaPostComments2 = await getComments(alpha, alphaPost.post_view.post.id);
|
||||
let alphaPostComments2 = await getComments(
|
||||
alpha,
|
||||
alphaPost.post_view.post.id
|
||||
);
|
||||
expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent);
|
||||
expect(alphaPostComments2.comments[0].community.local).toBe(true);
|
||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
||||
assertCommentFederation(alphaPostComments2.comments[0], commentRes.comment_view);
|
||||
assertCommentFederation(
|
||||
alphaPostComments2.comments[0],
|
||||
commentRes.comment_view
|
||||
);
|
||||
|
||||
// Make sure beta has mentions
|
||||
let mentionsRes = await getMentions(beta);
|
||||
|
@ -331,28 +370,32 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t
|
|||
// expect(mentionsRes.mentions[0].score).toBe(1);
|
||||
});
|
||||
|
||||
test('Check that activity from another instance is sent to third instance', async () => {
|
||||
test("Check that activity from another instance is sent to third instance", async () => {
|
||||
// Alpha and gamma users follow beta community
|
||||
let alphaFollow = await followBeta(alpha);
|
||||
expect(alphaFollow.community_view.community.local).toBe(false);
|
||||
expect(alphaFollow.community_view.community.name).toBe('main');
|
||||
expect(alphaFollow.community_view.community.name).toBe("main");
|
||||
|
||||
let gammaFollow = await followBeta(gamma);
|
||||
expect(gammaFollow.community_view.community.local).toBe(false);
|
||||
expect(gammaFollow.community_view.community.name).toBe('main');
|
||||
expect(gammaFollow.community_view.community.name).toBe("main");
|
||||
|
||||
// Create a post on beta
|
||||
let betaPost = await createPost(beta, 2);
|
||||
expect(betaPost.post_view.community.local).toBe(true);
|
||||
|
||||
// Make sure gamma and alpha see it
|
||||
let gammaPost = (await resolvePost(gamma, betaPost.post_view.post)).post.unwrap();
|
||||
let gammaPost = (
|
||||
await resolvePost(gamma, betaPost.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(gammaPost.post).toBeDefined();
|
||||
let alphaPost = (await resolvePost(alpha, betaPost.post_view.post)).post.unwrap();
|
||||
let alphaPost = (
|
||||
await resolvePost(alpha, betaPost.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(alphaPost.post).toBeDefined();
|
||||
|
||||
// The bug: gamma comments, and alpha should see it.
|
||||
let commentContent = 'Comment from gamma';
|
||||
let commentContent = "Comment from gamma";
|
||||
let commentRes = await createComment(
|
||||
gamma,
|
||||
gammaPost.post.id,
|
||||
|
@ -370,13 +413,16 @@ test('Check that activity from another instance is sent to third instance', asyn
|
|||
expect(alphaPostComments2.comments[0].community.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].creator.local).toBe(false);
|
||||
expect(alphaPostComments2.comments[0].counts.score).toBe(1);
|
||||
assertCommentFederation(alphaPostComments2.comments[0], commentRes.comment_view);
|
||||
assertCommentFederation(
|
||||
alphaPostComments2.comments[0],
|
||||
commentRes.comment_view
|
||||
);
|
||||
|
||||
await unfollowRemotes(alpha);
|
||||
await unfollowRemotes(gamma);
|
||||
});
|
||||
|
||||
test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.', async () => {
|
||||
test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.", async () => {
|
||||
// Unfollow all remote communities
|
||||
let site = await unfollowRemotes(alpha);
|
||||
expect(
|
||||
|
@ -387,7 +433,7 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
|||
let postRes = await createPost(beta, 2);
|
||||
expect(postRes.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(
|
||||
beta,
|
||||
postRes.post_view.post.id,
|
||||
|
@ -399,7 +445,7 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
|||
);
|
||||
|
||||
// B creates a comment, then a child one of that.
|
||||
let childCommentContent = 'An invisible child comment from beta';
|
||||
let childCommentContent = "An invisible child comment from beta";
|
||||
let childCommentRes = await createComment(
|
||||
beta,
|
||||
postRes.post_view.post.id,
|
||||
|
@ -413,50 +459,62 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde
|
|||
// Follow beta again
|
||||
let follow = await followBeta(alpha);
|
||||
expect(follow.community_view.community.local).toBe(false);
|
||||
expect(follow.community_view.community.name).toBe('main');
|
||||
expect(follow.community_view.community.name).toBe("main");
|
||||
|
||||
// An update to the child comment on beta, should push the post, parent, and child to alpha now
|
||||
let updatedCommentContent = 'An update child comment from beta';
|
||||
let updatedCommentContent = Some("An update child comment from beta");
|
||||
let updateRes = await editComment(
|
||||
beta,
|
||||
childCommentRes.comment_view.comment.id,
|
||||
updatedCommentContent
|
||||
);
|
||||
expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent);
|
||||
expect(updateRes.comment_view.comment.content).toBe(
|
||||
updatedCommentContent.unwrap()
|
||||
);
|
||||
|
||||
// Get the post from alpha
|
||||
let alphaPostB = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap();
|
||||
let alphaPostB = (
|
||||
await resolvePost(alpha, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
|
||||
let alphaPost = await getPost(alpha, alphaPostB.post.id);
|
||||
let alphaPostComments = await getComments(alpha, alphaPostB.post.id);
|
||||
expect(alphaPost.post_view.post.name).toBeDefined();
|
||||
assertCommentFederation(alphaPostComments.comments[1], parentCommentRes.comment_view);
|
||||
assertCommentFederation(alphaPostComments.comments[0], updateRes.comment_view);
|
||||
assertCommentFederation(
|
||||
alphaPostComments.comments[1],
|
||||
parentCommentRes.comment_view
|
||||
);
|
||||
assertCommentFederation(
|
||||
alphaPostComments.comments[0],
|
||||
updateRes.comment_view
|
||||
);
|
||||
expect(alphaPost.post_view.community.local).toBe(false);
|
||||
expect(alphaPost.post_view.creator.local).toBe(false);
|
||||
|
||||
await unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
|
||||
test('Report a comment', async () => {
|
||||
test("Report a comment", async () => {
|
||||
let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap();
|
||||
let postRes = (await createPost(beta, betaCommunity.community.id)).post_view.post;
|
||||
let postRes = (await createPost(beta, betaCommunity.community.id)).post_view
|
||||
.post;
|
||||
expect(postRes).toBeDefined();
|
||||
let commentRes = (await createComment(beta, postRes.id, None)).comment_view.comment;
|
||||
let commentRes = (await createComment(beta, postRes.id, None)).comment_view
|
||||
.comment;
|
||||
expect(commentRes).toBeDefined();
|
||||
|
||||
let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap().comment;
|
||||
let alphaReport = (await reportComment(alpha, alphaComment.id, randomString(10)))
|
||||
.comment_report_view.comment_report;
|
||||
let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap()
|
||||
.comment;
|
||||
let alphaReport = (
|
||||
await reportComment(alpha, alphaComment.id, randomString(10))
|
||||
).comment_report_view.comment_report;
|
||||
|
||||
let betaReport = (await listCommentReports(beta)).comment_reports[0].comment_report;
|
||||
let betaReport = (await listCommentReports(beta)).comment_reports[0]
|
||||
.comment_report;
|
||||
expect(betaReport).toBeDefined();
|
||||
expect(betaReport.resolved).toBe(false);
|
||||
expect(betaReport.original_comment_text).toBe(alphaReport.original_comment_text);
|
||||
expect(betaReport.original_comment_text).toBe(
|
||||
alphaReport.original_comment_text
|
||||
);
|
||||
expect(betaReport.reason).toBe(alphaReport.reason);
|
||||
});
|
||||
function N(gamma: API, id: number, N: any, commentContent: string) {
|
||||
throw new Error('Function not implemented.');
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
jest.setTimeout(120000);
|
||||
import { CommunityView } from 'lemmy-js-client';
|
||||
import { CommunityView } from "lemmy-js-client";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
|
@ -18,7 +18,7 @@ import {
|
|||
createPost,
|
||||
getPost,
|
||||
resolvePost,
|
||||
} from './shared';
|
||||
} from "./shared";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
|
@ -34,8 +34,12 @@ function assertCommunityFederation(
|
|||
expect(communityOne.community.description.unwrapOr("none")).toBe(
|
||||
communityTwo.community.description.unwrapOr("none")
|
||||
);
|
||||
expect(communityOne.community.icon.unwrapOr("none")).toBe(communityTwo.community.icon.unwrapOr("none"));
|
||||
expect(communityOne.community.banner.unwrapOr("none")).toBe(communityTwo.community.banner.unwrapOr("none"));
|
||||
expect(communityOne.community.icon.unwrapOr("none")).toBe(
|
||||
communityTwo.community.icon.unwrapOr("none")
|
||||
);
|
||||
expect(communityOne.community.banner.unwrapOr("none")).toBe(
|
||||
communityTwo.community.banner.unwrapOr("none")
|
||||
);
|
||||
expect(communityOne.community.published).toBe(
|
||||
communityTwo.community.published
|
||||
);
|
||||
|
@ -44,35 +48,35 @@ function assertCommunityFederation(
|
|||
expect(communityOne.community.deleted).toBe(communityTwo.community.deleted);
|
||||
}
|
||||
|
||||
test('Create community', async () => {
|
||||
test("Create community", async () => {
|
||||
let communityRes = await createCommunity(alpha);
|
||||
expect(communityRes.community_view.community.name).toBeDefined();
|
||||
|
||||
// A dupe check
|
||||
let prevName = communityRes.community_view.community.name;
|
||||
let communityRes2: any = await createCommunity(alpha, prevName);
|
||||
expect(communityRes2['error']).toBe('community_already_exists');
|
||||
expect(communityRes2["error"]).toBe("community_already_exists");
|
||||
|
||||
// Cache the community on beta, make sure it has the other fields
|
||||
let searchShort = `!${prevName}@lemmy-alpha:8541`;
|
||||
let betaCommunity = (await resolveCommunity(beta, searchShort)).community.unwrap();
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, searchShort)
|
||||
).community.unwrap();
|
||||
assertCommunityFederation(betaCommunity, communityRes.community_view);
|
||||
});
|
||||
|
||||
test('Delete community', async () => {
|
||||
test("Delete community", async () => {
|
||||
let communityRes = await createCommunity(beta);
|
||||
|
||||
// Cache the community on Alpha
|
||||
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
|
||||
let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap();
|
||||
let alphaCommunity = (
|
||||
await resolveCommunity(alpha, searchShort)
|
||||
).community.unwrap();
|
||||
assertCommunityFederation(alphaCommunity, communityRes.community_view);
|
||||
|
||||
// Follow the community from alpha
|
||||
let follow = await followCommunity(
|
||||
alpha,
|
||||
true,
|
||||
alphaCommunity.community.id
|
||||
);
|
||||
let follow = await followCommunity(alpha, true, alphaCommunity.community.id);
|
||||
|
||||
// Make sure the follow response went through
|
||||
expect(follow.community_view.community.local).toBe(false);
|
||||
|
@ -83,7 +87,9 @@ test('Delete community', async () => {
|
|||
communityRes.community_view.community.id
|
||||
);
|
||||
expect(deleteCommunityRes.community_view.community.deleted).toBe(true);
|
||||
expect(deleteCommunityRes.community_view.community.title).toBe(communityRes.community_view.community.title);
|
||||
expect(deleteCommunityRes.community_view.community.title).toBe(
|
||||
communityRes.community_view.community.title
|
||||
);
|
||||
|
||||
// Make sure it got deleted on A
|
||||
let communityOnAlphaDeleted = await getCommunity(
|
||||
|
@ -110,20 +116,18 @@ test('Delete community', async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('Remove community', async () => {
|
||||
test("Remove community", async () => {
|
||||
let communityRes = await createCommunity(beta);
|
||||
|
||||
// Cache the community on Alpha
|
||||
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
|
||||
let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap();
|
||||
let alphaCommunity = (
|
||||
await resolveCommunity(alpha, searchShort)
|
||||
).community.unwrap();
|
||||
assertCommunityFederation(alphaCommunity, communityRes.community_view);
|
||||
|
||||
// Follow the community from alpha
|
||||
let follow = await followCommunity(
|
||||
alpha,
|
||||
true,
|
||||
alphaCommunity.community.id
|
||||
);
|
||||
let follow = await followCommunity(alpha, true, alphaCommunity.community.id);
|
||||
|
||||
// Make sure the follow response went through
|
||||
expect(follow.community_view.community.local).toBe(false);
|
||||
|
@ -134,7 +138,9 @@ test('Remove community', async () => {
|
|||
communityRes.community_view.community.id
|
||||
);
|
||||
expect(removeCommunityRes.community_view.community.removed).toBe(true);
|
||||
expect(removeCommunityRes.community_view.community.title).toBe(communityRes.community_view.community.title);
|
||||
expect(removeCommunityRes.community_view.community.title).toBe(
|
||||
communityRes.community_view.community.title
|
||||
);
|
||||
|
||||
// Make sure it got Removed on A
|
||||
let communityOnAlphaRemoved = await getCommunity(
|
||||
|
@ -161,34 +167,53 @@ test('Remove community', async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('Search for beta community', async () => {
|
||||
test("Search for beta community", async () => {
|
||||
let communityRes = await createCommunity(beta);
|
||||
expect(communityRes.community_view.community.name).toBeDefined();
|
||||
|
||||
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`;
|
||||
let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap();
|
||||
let alphaCommunity = (
|
||||
await resolveCommunity(alpha, searchShort)
|
||||
).community.unwrap();
|
||||
assertCommunityFederation(alphaCommunity, communityRes.community_view);
|
||||
});
|
||||
|
||||
test('Admin actions in remote community are not federated to origin', async () => {
|
||||
test("Admin actions in remote community are not federated to origin", async () => {
|
||||
// create a community on alpha
|
||||
let communityRes = (await createCommunity(alpha)).community_view;
|
||||
expect(communityRes.community.name).toBeDefined();
|
||||
|
||||
// gamma follows community and posts in it
|
||||
let gammaCommunity = (await resolveCommunity(gamma, communityRes.community.actor_id)).community.unwrap();
|
||||
let gammaFollow = (await followCommunity(gamma, true, gammaCommunity.community.id));
|
||||
let gammaCommunity = (
|
||||
await resolveCommunity(gamma, communityRes.community.actor_id)
|
||||
).community.unwrap();
|
||||
let gammaFollow = await followCommunity(
|
||||
gamma,
|
||||
true,
|
||||
gammaCommunity.community.id
|
||||
);
|
||||
expect(gammaFollow.community_view.subscribed).toBe("Subscribed");
|
||||
let gammaPost = (await createPost(gamma, gammaCommunity.community.id)).post_view;
|
||||
let gammaPost = (await createPost(gamma, gammaCommunity.community.id))
|
||||
.post_view;
|
||||
expect(gammaPost.post.id).toBeDefined();
|
||||
expect(gammaPost.creator_banned_from_community).toBe(false);
|
||||
|
||||
// admin of beta decides to ban gamma from community
|
||||
let betaCommunity = (await resolveCommunity(beta, communityRes.community.actor_id)).community.unwrap();
|
||||
let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view.person;
|
||||
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.actor_id)).person.unwrap();
|
||||
let banRes = (await banPersonFromCommunity(beta, bannedUserInfo2.person.id, betaCommunity.community.id, true, true));
|
||||
console.log(banRes);
|
||||
let betaCommunity = (
|
||||
await resolveCommunity(beta, communityRes.community.actor_id)
|
||||
).community.unwrap();
|
||||
let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view
|
||||
.person;
|
||||
let bannedUserInfo2 = (
|
||||
await resolvePerson(beta, bannedUserInfo1.actor_id)
|
||||
).person.unwrap();
|
||||
let banRes = await banPersonFromCommunity(
|
||||
beta,
|
||||
bannedUserInfo2.person.id,
|
||||
betaCommunity.community.id,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(banRes.banned).toBe(true);
|
||||
|
||||
// ban doesnt federate to community's origin instance alpha
|
||||
|
@ -196,6 +221,6 @@ test('Admin actions in remote community are not federated to origin', async () =
|
|||
expect(alphaPost.creator_banned_from_community).toBe(false);
|
||||
|
||||
// and neither to gamma
|
||||
let gammaPost2 = (await getPost(gamma, gammaPost.post.id));
|
||||
let gammaPost2 = await getPost(gamma, gammaPost.post.id);
|
||||
expect(gammaPost2.post_view.creator_banned_from_community).toBe(false);
|
||||
});
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
jest.setTimeout(120000);
|
||||
import {SubscribedType} from 'lemmy-js-client';
|
||||
import { SubscribedType } from "lemmy-js-client";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
setupLogins,
|
||||
|
@ -7,8 +8,7 @@ import {
|
|||
followCommunity,
|
||||
unfollowRemotes,
|
||||
getSite,
|
||||
delay,
|
||||
} from './shared';
|
||||
} from "./shared";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
|
@ -18,24 +18,20 @@ afterAll(async () => {
|
|||
await unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
test('Follow federated community', async () => {
|
||||
test("Follow federated community", async () => {
|
||||
let betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap();
|
||||
let follow = await followCommunity(
|
||||
alpha,
|
||||
true,
|
||||
betaCommunity.community.id
|
||||
);
|
||||
let follow = await followCommunity(alpha, true, betaCommunity.community.id);
|
||||
|
||||
// Make sure the follow response went through
|
||||
expect(follow.community_view.community.local).toBe(false);
|
||||
expect(follow.community_view.community.name).toBe('main');
|
||||
expect(follow.community_view.community.name).toBe("main");
|
||||
expect(follow.community_view.subscribed).toBe(SubscribedType.Subscribed);
|
||||
|
||||
// Check it from local
|
||||
let site = await getSite(alpha);
|
||||
let remoteCommunityId = site.my_user.unwrap().follows.find(
|
||||
c => c.community.local == false
|
||||
).community.id;
|
||||
let remoteCommunityId = site.my_user
|
||||
.unwrap()
|
||||
.follows.find(c => c.community.local == false).community.id;
|
||||
expect(remoteCommunityId).toBeDefined();
|
||||
expect(site.my_user.unwrap().follows.length).toBe(2);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
jest.setTimeout(120000);
|
||||
import {None} from '@sniptt/monads';
|
||||
import { PostView, CommunityView } from 'lemmy-js-client';
|
||||
import { None } from "@sniptt/monads";
|
||||
import { PostView, CommunityView } from "lemmy-js-client";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
beta,
|
||||
|
@ -33,8 +34,8 @@ import {
|
|||
API,
|
||||
getSite,
|
||||
unfollows,
|
||||
resolveCommunity
|
||||
} from './shared';
|
||||
resolveCommunity,
|
||||
} from "./shared";
|
||||
|
||||
let betaCommunity: CommunityView;
|
||||
|
||||
|
@ -52,12 +53,22 @@ afterAll(async () => {
|
|||
function assertPostFederation(postOne: PostView, postTwo: PostView) {
|
||||
expect(postOne.post.ap_id).toBe(postTwo.post.ap_id);
|
||||
expect(postOne.post.name).toBe(postTwo.post.name);
|
||||
expect(postOne.post.body.unwrapOr("none")).toBe(postTwo.post.body.unwrapOr("none"));
|
||||
expect(postOne.post.url.unwrapOr("none")).toBe(postTwo.post.url.unwrapOr("none"));
|
||||
expect(postOne.post.body.unwrapOr("none")).toBe(
|
||||
postTwo.post.body.unwrapOr("none")
|
||||
);
|
||||
expect(postOne.post.url.unwrapOr("https://google.com/")).toBe(
|
||||
postTwo.post.url.unwrapOr("https://google.com/")
|
||||
);
|
||||
expect(postOne.post.nsfw).toBe(postTwo.post.nsfw);
|
||||
expect(postOne.post.embed_title.unwrapOr("none")).toBe(postTwo.post.embed_title.unwrapOr("none"));
|
||||
expect(postOne.post.embed_description.unwrapOr("none")).toBe(postTwo.post.embed_description.unwrapOr("none"));
|
||||
expect(postOne.post.embed_html.unwrapOr("none")).toBe(postTwo.post.embed_html.unwrapOr("none"));
|
||||
expect(postOne.post.embed_title.unwrapOr("none")).toBe(
|
||||
postTwo.post.embed_title.unwrapOr("none")
|
||||
);
|
||||
expect(postOne.post.embed_description.unwrapOr("none")).toBe(
|
||||
postTwo.post.embed_description.unwrapOr("none")
|
||||
);
|
||||
expect(postOne.post.embed_video_url.unwrapOr("none")).toBe(
|
||||
postTwo.post.embed_video_url.unwrapOr("none")
|
||||
);
|
||||
expect(postOne.post.published).toBe(postTwo.post.published);
|
||||
expect(postOne.community.actor_id).toBe(postTwo.community.actor_id);
|
||||
expect(postOne.post.locked).toBe(postTwo.post.locked);
|
||||
|
@ -65,7 +76,7 @@ function assertPostFederation(postOne: PostView, postTwo: PostView) {
|
|||
expect(postOne.post.deleted).toBe(postTwo.post.deleted);
|
||||
}
|
||||
|
||||
test('Create a post', async () => {
|
||||
test("Create a post", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
expect(postRes.post_view.community.local).toBe(false);
|
||||
|
@ -73,7 +84,9 @@ test('Create a post', async () => {
|
|||
expect(postRes.post_view.counts.score).toBe(1);
|
||||
|
||||
// Make sure that post is liked on beta
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
|
||||
expect(betaPost).toBeDefined();
|
||||
expect(betaPost.community.local).toBe(true);
|
||||
|
@ -83,19 +96,19 @@ test('Create a post', async () => {
|
|||
|
||||
// Delta only follows beta, so it should not see an alpha ap_id
|
||||
let deltaPost = (await resolvePost(delta, postRes.post_view.post)).post;
|
||||
expect(deltaPost.isNone()).toBe(true)
|
||||
expect(deltaPost.isNone()).toBe(true);
|
||||
|
||||
// Epsilon has alpha blocked, it should not see the alpha post
|
||||
let epsilonPost = (await resolvePost(epsilon, postRes.post_view.post)).post;
|
||||
expect(epsilonPost.isNone()).toBe(true);
|
||||
});
|
||||
|
||||
test('Create a post in a non-existent community', async () => {
|
||||
let postRes = await createPost(alpha, -2) as any;
|
||||
expect(postRes.error).toBe('couldnt_find_community');
|
||||
test("Create a post in a non-existent community", async () => {
|
||||
let postRes = (await createPost(alpha, -2)) as any;
|
||||
expect(postRes.error).toBe("couldnt_find_community");
|
||||
});
|
||||
|
||||
test('Unlike a post', async () => {
|
||||
test("Unlike a post", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
let unlike = await likePost(alpha, 0, postRes.post_view.post);
|
||||
expect(unlike.post_view.counts.score).toBe(0);
|
||||
|
@ -105,7 +118,9 @@ test('Unlike a post', async () => {
|
|||
expect(unlike2.post_view.counts.score).toBe(0);
|
||||
|
||||
// Make sure that post is unliked on beta
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost).toBeDefined();
|
||||
expect(betaPost.community.local).toBe(true);
|
||||
expect(betaPost.creator.local).toBe(false);
|
||||
|
@ -113,36 +128,42 @@ test('Unlike a post', async () => {
|
|||
assertPostFederation(betaPost, postRes.post_view);
|
||||
});
|
||||
|
||||
test('Update a post', async () => {
|
||||
test("Update a post", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
|
||||
let updatedName = 'A jest test federated post, updated';
|
||||
let updatedName = "A jest test federated post, updated";
|
||||
let updatedPost = await editPost(alpha, postRes.post_view.post);
|
||||
expect(updatedPost.post_view.post.name).toBe(updatedName);
|
||||
expect(updatedPost.post_view.community.local).toBe(false);
|
||||
expect(updatedPost.post_view.creator.local).toBe(true);
|
||||
|
||||
// Make sure that post is updated on beta
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost.community.local).toBe(true);
|
||||
expect(betaPost.creator.local).toBe(false);
|
||||
expect(betaPost.post.name).toBe(updatedName);
|
||||
assertPostFederation(betaPost, updatedPost.post_view);
|
||||
|
||||
// Make sure lemmy beta cannot update the post
|
||||
let updatedPostBeta = await editPost(beta, betaPost.post) as any;
|
||||
expect(updatedPostBeta.error).toBe('no_post_edit_allowed');
|
||||
let updatedPostBeta = (await editPost(beta, betaPost.post)) as any;
|
||||
expect(updatedPostBeta.error).toBe("no_post_edit_allowed");
|
||||
});
|
||||
|
||||
test('Sticky a post', async () => {
|
||||
test("Sticky a post", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
|
||||
let betaPost1 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost1 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
let stickiedPostRes = await stickyPost(beta, true, betaPost1.post);
|
||||
expect(stickiedPostRes.post_view.post.stickied).toBe(true);
|
||||
|
||||
// Make sure that post is stickied on beta
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost.community.local).toBe(true);
|
||||
expect(betaPost.creator.local).toBe(false);
|
||||
expect(betaPost.post.stickied).toBe(true);
|
||||
|
@ -152,25 +173,33 @@ test('Sticky a post', async () => {
|
|||
expect(unstickiedPost.post_view.post.stickied).toBe(false);
|
||||
|
||||
// Make sure that post is unstickied on beta
|
||||
let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost2 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost2.community.local).toBe(true);
|
||||
expect(betaPost2.creator.local).toBe(false);
|
||||
expect(betaPost2.post.stickied).toBe(false);
|
||||
|
||||
// Make sure that gamma cannot sticky the post on beta
|
||||
let gammaPost = (await resolvePost(gamma, postRes.post_view.post)).post.unwrap();
|
||||
let gammaPost = (
|
||||
await resolvePost(gamma, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post);
|
||||
let betaPost3 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost3 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(gammaTrySticky.post_view.post.stickied).toBe(true);
|
||||
expect(betaPost3.post.stickied).toBe(false);
|
||||
});
|
||||
|
||||
test('Lock a post', async () => {
|
||||
test("Lock a post", async () => {
|
||||
await followCommunity(alpha, true, betaCommunity.community.id);
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
|
||||
// Lock the post
|
||||
let betaPost1 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost1 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
let lockedPostRes = await lockPost(beta, true, betaPost1.post);
|
||||
expect(lockedPostRes.post_view.post.locked).toBe(true);
|
||||
|
||||
|
@ -181,7 +210,7 @@ test('Lock a post', async () => {
|
|||
|
||||
// Try to make a new comment there, on alpha
|
||||
let comment: any = await createComment(alpha, alphaPost1.post.id, None);
|
||||
expect(comment['error']).toBe('locked');
|
||||
expect(comment["error"]).toBe("locked");
|
||||
|
||||
// Unlock a post
|
||||
let unlockedPost = await lockPost(beta, false, betaPost1.post);
|
||||
|
@ -199,7 +228,7 @@ test('Lock a post', async () => {
|
|||
expect(commentAlpha).toBeDefined();
|
||||
});
|
||||
|
||||
test('Delete a post', async () => {
|
||||
test("Delete a post", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
|
@ -217,26 +246,38 @@ test('Delete a post', async () => {
|
|||
expect(undeletedPost.post_view.post.deleted).toBe(false);
|
||||
|
||||
// Make sure lemmy beta sees post is undeleted
|
||||
let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost2 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost2.post.deleted).toBe(false);
|
||||
assertPostFederation(betaPost2, undeletedPost.post_view);
|
||||
|
||||
// Make sure lemmy beta cannot delete the post
|
||||
let deletedPostBeta = await deletePost(beta, true, betaPost2.post) as any;
|
||||
expect(deletedPostBeta.error).toStrictEqual('no_post_edit_allowed');
|
||||
let deletedPostBeta = (await deletePost(beta, true, betaPost2.post)) as any;
|
||||
expect(deletedPostBeta.error).toStrictEqual("no_post_edit_allowed");
|
||||
});
|
||||
|
||||
test('Remove a post from admin and community on different instance', async () => {
|
||||
let gammaCommunity = await resolveCommunity(gamma, betaCommunity.community.actor_id);
|
||||
let postRes = await createPost(gamma, gammaCommunity.community.unwrap().community.id);
|
||||
test("Remove a post from admin and community on different instance", async () => {
|
||||
let gammaCommunity = await resolveCommunity(
|
||||
gamma,
|
||||
betaCommunity.community.actor_id
|
||||
);
|
||||
let postRes = await createPost(
|
||||
gamma,
|
||||
gammaCommunity.community.unwrap().community.id
|
||||
);
|
||||
|
||||
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap();
|
||||
let alphaPost = (
|
||||
await resolvePost(alpha, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
let removedPost = await removePost(alpha, true, alphaPost.post);
|
||||
expect(removedPost.post_view.post.removed).toBe(true);
|
||||
expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name);
|
||||
|
||||
// Make sure lemmy beta sees post is NOT removed
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost.post.removed).toBe(false);
|
||||
|
||||
// Undelete
|
||||
|
@ -244,12 +285,14 @@ test('Remove a post from admin and community on different instance', async () =>
|
|||
expect(undeletedPost.post_view.post.removed).toBe(false);
|
||||
|
||||
// Make sure lemmy beta sees post is undeleted
|
||||
let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost2 = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost2.post.removed).toBe(false);
|
||||
assertPostFederation(betaPost2, undeletedPost.post_view);
|
||||
});
|
||||
|
||||
test('Remove a post from admin and community on same instance', async () => {
|
||||
test("Remove a post from admin and community on same instance", async () => {
|
||||
await followBeta(alpha);
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
@ -279,16 +322,18 @@ test('Remove a post from admin and community on same instance', async () => {
|
|||
await unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
test('Search for a post', async () => {
|
||||
test("Search for a post", async () => {
|
||||
await unfollowRemotes(alpha);
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(beta, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost.post.name).toBeDefined();
|
||||
});
|
||||
|
||||
test('Enforce site ban for federated user', async () => {
|
||||
test("Enforce site ban for federated user", async () => {
|
||||
// create a test user
|
||||
let alphaUserJwt = await registerUser(alpha);
|
||||
expect(alphaUserJwt).toBeDefined();
|
||||
|
@ -296,9 +341,12 @@ test('Enforce site ban for federated user', async () => {
|
|||
client: alpha.client,
|
||||
auth: alphaUserJwt.jwt,
|
||||
};
|
||||
let alphaUserActorId = (await getSite(alpha_user)).my_user.unwrap().local_user_view.person.actor_id;
|
||||
let alphaUserActorId = (await getSite(alpha_user)).my_user.unwrap()
|
||||
.local_user_view.person.actor_id;
|
||||
expect(alphaUserActorId).toBeDefined();
|
||||
let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId)).person.unwrap();
|
||||
let alphaPerson = (
|
||||
await resolvePerson(alpha_user, alphaUserActorId)
|
||||
).person.unwrap();
|
||||
expect(alphaPerson).toBeDefined();
|
||||
|
||||
// alpha makes post in beta community, it federates to beta instance
|
||||
|
@ -307,7 +355,12 @@ test('Enforce site ban for federated user', async () => {
|
|||
expect(searchBeta1.posts[0]).toBeDefined();
|
||||
|
||||
// ban alpha from its instance
|
||||
let banAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, true, true);
|
||||
let banAlpha = await banPersonFromSite(
|
||||
alpha,
|
||||
alphaPerson.person.id,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(banAlpha.banned).toBe(true);
|
||||
|
||||
// alpha ban should be federated to beta
|
||||
|
@ -319,7 +372,12 @@ test('Enforce site ban for federated user', async () => {
|
|||
expect(searchBeta2.posts[0]).toBeUndefined();
|
||||
|
||||
// Unban alpha
|
||||
let unBanAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, false, false);
|
||||
let unBanAlpha = await banPersonFromSite(
|
||||
alpha,
|
||||
alphaPerson.person.id,
|
||||
false,
|
||||
false
|
||||
);
|
||||
expect(unBanAlpha.banned).toBe(false);
|
||||
|
||||
// alpha makes new post in beta community, it federates
|
||||
|
@ -327,11 +385,11 @@ test('Enforce site ban for federated user', async () => {
|
|||
let searchBeta3 = await searchPostLocal(beta, postRes2.post_view.post);
|
||||
expect(searchBeta3.posts[0]).toBeDefined();
|
||||
|
||||
let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId)
|
||||
let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId);
|
||||
expect(alphaUserOnBeta2.person.unwrap().person.banned).toBe(false);
|
||||
});
|
||||
|
||||
test('Enforce community ban for federated user', async () => {
|
||||
test("Enforce community ban for federated user", async () => {
|
||||
let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
|
||||
let alphaPerson = (await resolvePerson(beta, alphaShortname)).person.unwrap();
|
||||
expect(alphaPerson).toBeDefined();
|
||||
|
@ -342,7 +400,13 @@ test('Enforce community ban for federated user', async () => {
|
|||
expect(searchBeta1.posts[0]).toBeDefined();
|
||||
|
||||
// ban alpha from beta community
|
||||
let banAlpha = await banPersonFromCommunity(beta, alphaPerson.person.id, 2, true, true);
|
||||
let banAlpha = await banPersonFromCommunity(
|
||||
beta,
|
||||
alphaPerson.person.id,
|
||||
2,
|
||||
true,
|
||||
true
|
||||
);
|
||||
expect(banAlpha.banned).toBe(true);
|
||||
|
||||
// ensure that the post by alpha got removed
|
||||
|
@ -373,29 +437,37 @@ test('Enforce community ban for federated user', async () => {
|
|||
expect(searchBeta2.posts[0]).toBeDefined();
|
||||
});
|
||||
|
||||
|
||||
test('A and G subscribe to B (center) A posts, it gets announced to G', async () => {
|
||||
test("A and G subscribe to B (center) A posts, it gets announced to G", async () => {
|
||||
let postRes = await createPost(alpha, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
let betaPost = (await resolvePost(gamma, postRes.post_view.post)).post.unwrap();
|
||||
let betaPost = (
|
||||
await resolvePost(gamma, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
expect(betaPost.post.name).toBeDefined();
|
||||
});
|
||||
|
||||
test('Report a post', async () => {
|
||||
test("Report a post", async () => {
|
||||
let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap();
|
||||
let postRes = await createPost(beta, betaCommunity.community.id);
|
||||
expect(postRes.post_view.post).toBeDefined();
|
||||
|
||||
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap();
|
||||
let alphaReport = (await reportPost(alpha, alphaPost.post.id, randomString(10)))
|
||||
.post_report_view.post_report;
|
||||
let alphaPost = (
|
||||
await resolvePost(alpha, postRes.post_view.post)
|
||||
).post.unwrap();
|
||||
let alphaReport = (
|
||||
await reportPost(alpha, alphaPost.post.id, randomString(10))
|
||||
).post_report_view.post_report;
|
||||
|
||||
let betaReport = (await listPostReports(beta)).post_reports[0].post_report;
|
||||
expect(betaReport).toBeDefined();
|
||||
expect(betaReport.resolved).toBe(false);
|
||||
expect(betaReport.original_post_name).toBe(alphaReport.original_post_name);
|
||||
expect(betaReport.original_post_url.unwrapOr("none")).toBe(alphaReport.original_post_url.unwrapOr("none"));
|
||||
expect(betaReport.original_post_body.unwrapOr("none")).toBe(alphaReport.original_post_body.unwrapOr("none"));
|
||||
expect(betaReport.original_post_url.unwrapOr("none")).toBe(
|
||||
alphaReport.original_post_url.unwrapOr("none")
|
||||
);
|
||||
expect(betaReport.original_post_body.unwrapOr("none")).toBe(
|
||||
alphaReport.original_post_body.unwrapOr("none")
|
||||
);
|
||||
expect(betaReport.reason).toBe(alphaReport.reason);
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@ import {
|
|||
listPrivateMessages,
|
||||
deletePrivateMessage,
|
||||
unfollowRemotes,
|
||||
} from './shared';
|
||||
} from "./shared";
|
||||
|
||||
let recipient_id: number;
|
||||
|
||||
|
@ -23,7 +23,7 @@ afterAll(async () => {
|
|||
await unfollowRemotes(alpha);
|
||||
});
|
||||
|
||||
test('Create a private message', async () => {
|
||||
test("Create a private message", async () => {
|
||||
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||
expect(pmRes.private_message_view.private_message.content).toBeDefined();
|
||||
expect(pmRes.private_message_view.private_message.local).toBe(true);
|
||||
|
@ -37,8 +37,8 @@ test('Create a private message', async () => {
|
|||
expect(betaPms.private_messages[0].recipient.local).toBe(true);
|
||||
});
|
||||
|
||||
test('Update a private message', async () => {
|
||||
let updatedContent = 'A jest test federated private message edited';
|
||||
test("Update a private message", async () => {
|
||||
let updatedContent = "A jest test federated private message edited";
|
||||
|
||||
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||
let pmUpdated = await editPrivateMessage(
|
||||
|
@ -55,7 +55,7 @@ test('Update a private message', async () => {
|
|||
);
|
||||
});
|
||||
|
||||
test('Delete a private message', async () => {
|
||||
test("Delete a private message", async () => {
|
||||
let pmRes = await createPrivateMessage(alpha, recipient_id);
|
||||
let betaPms1 = await listPrivateMessages(beta);
|
||||
let deletedPmRes = await deletePrivateMessage(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import {None, Some, Option} from '@sniptt/monads';
|
||||
import { None, Some, Option } from "@sniptt/monads";
|
||||
import {
|
||||
Login,
|
||||
LoginResponse,
|
||||
|
@ -63,8 +63,8 @@ import {
|
|||
EditSite,
|
||||
CommentSortType,
|
||||
GetComments,
|
||||
GetCommentsResponse
|
||||
} from 'lemmy-js-client';
|
||||
GetCommentsResponse,
|
||||
} from "lemmy-js-client";
|
||||
|
||||
export interface API {
|
||||
client: LemmyHttp;
|
||||
|
@ -72,59 +72,59 @@ export interface API {
|
|||
}
|
||||
|
||||
export let alpha: API = {
|
||||
client: new LemmyHttp('http://127.0.0.1:8541'),
|
||||
client: new LemmyHttp("http://127.0.0.1:8541"),
|
||||
auth: None,
|
||||
};
|
||||
|
||||
export let beta: API = {
|
||||
client: new LemmyHttp('http://127.0.0.1:8551'),
|
||||
client: new LemmyHttp("http://127.0.0.1:8551"),
|
||||
auth: None,
|
||||
};
|
||||
|
||||
export let gamma: API = {
|
||||
client: new LemmyHttp('http://127.0.0.1:8561'),
|
||||
client: new LemmyHttp("http://127.0.0.1:8561"),
|
||||
auth: None,
|
||||
};
|
||||
|
||||
export let delta: API = {
|
||||
client: new LemmyHttp('http://127.0.0.1:8571'),
|
||||
client: new LemmyHttp("http://127.0.0.1:8571"),
|
||||
auth: None,
|
||||
};
|
||||
|
||||
export let epsilon: API = {
|
||||
client: new LemmyHttp('http://127.0.0.1:8581'),
|
||||
client: new LemmyHttp("http://127.0.0.1:8581"),
|
||||
auth: None,
|
||||
};
|
||||
|
||||
const password = 'lemmylemmy'
|
||||
const password = "lemmylemmy";
|
||||
|
||||
export async function setupLogins() {
|
||||
let formAlpha = new Login({
|
||||
username_or_email: 'lemmy_alpha',
|
||||
username_or_email: "lemmy_alpha",
|
||||
password,
|
||||
});
|
||||
let resAlpha = alpha.client.login(formAlpha);
|
||||
|
||||
let formBeta = new Login({
|
||||
username_or_email: 'lemmy_beta',
|
||||
username_or_email: "lemmy_beta",
|
||||
password,
|
||||
});
|
||||
let resBeta = beta.client.login(formBeta);
|
||||
|
||||
let formGamma = new Login({
|
||||
username_or_email: 'lemmy_gamma',
|
||||
username_or_email: "lemmy_gamma",
|
||||
password,
|
||||
});
|
||||
let resGamma = gamma.client.login(formGamma);
|
||||
|
||||
let formDelta = new Login({
|
||||
username_or_email: 'lemmy_delta',
|
||||
username_or_email: "lemmy_delta",
|
||||
password,
|
||||
});
|
||||
let resDelta = delta.client.login(formDelta);
|
||||
|
||||
let formEpsilon = new Login({
|
||||
username_or_email: 'lemmy_epsilon',
|
||||
username_or_email: "lemmy_epsilon",
|
||||
password,
|
||||
});
|
||||
let resEpsilon = epsilon.client.login(formEpsilon);
|
||||
|
@ -145,6 +145,8 @@ export async function setupLogins() {
|
|||
|
||||
// Registration applications are now enabled by default, need to disable them
|
||||
let editSiteForm = new EditSite({
|
||||
require_application: Some(false),
|
||||
federation_debug: Some(true),
|
||||
name: None,
|
||||
sidebar: None,
|
||||
description: None,
|
||||
|
@ -155,23 +157,74 @@ export async function setupLogins() {
|
|||
enable_nsfw: None,
|
||||
community_creation_admin_only: None,
|
||||
require_email_verification: None,
|
||||
require_application: Some(false),
|
||||
application_question: None,
|
||||
private_instance: None,
|
||||
default_theme: None,
|
||||
legal_information: None,
|
||||
default_post_listing_type: None,
|
||||
legal_information: None,
|
||||
application_email_admins: None,
|
||||
hide_modlog_mod_names: None,
|
||||
discussion_languages: None,
|
||||
slur_filter_regex: None,
|
||||
actor_name_max_length: None,
|
||||
rate_limit_message: Some(999),
|
||||
rate_limit_message_per_second: None,
|
||||
rate_limit_post: Some(999),
|
||||
rate_limit_post_per_second: None,
|
||||
rate_limit_register: Some(999),
|
||||
rate_limit_register_per_second: None,
|
||||
rate_limit_image: Some(999),
|
||||
rate_limit_image_per_second: None,
|
||||
rate_limit_comment: Some(999),
|
||||
rate_limit_comment_per_second: None,
|
||||
rate_limit_search: Some(999),
|
||||
rate_limit_search_per_second: None,
|
||||
federation_enabled: None,
|
||||
federation_strict_allowlist: None,
|
||||
federation_http_fetch_retry_limit: None,
|
||||
federation_worker_count: None,
|
||||
captcha_enabled: None,
|
||||
captcha_difficulty: None,
|
||||
allowed_instances: None,
|
||||
blocked_instances: None,
|
||||
auth: "",
|
||||
});
|
||||
|
||||
// Set the blocks and auths for each
|
||||
editSiteForm.auth = alpha.auth.unwrap();
|
||||
editSiteForm.allowed_instances = Some([
|
||||
"lemmy-beta",
|
||||
"lemmy-gamma",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
]);
|
||||
await alpha.client.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.auth = beta.auth.unwrap();
|
||||
editSiteForm.allowed_instances = Some([
|
||||
"lemmy-alpha",
|
||||
"lemmy-gamma",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
]);
|
||||
await beta.client.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.auth = gamma.auth.unwrap();
|
||||
editSiteForm.allowed_instances = Some([
|
||||
"lemmy-alpha",
|
||||
"lemmy-beta",
|
||||
"lemmy-delta",
|
||||
"lemmy-epsilon",
|
||||
]);
|
||||
await gamma.client.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.allowed_instances = Some(["lemmy-beta"]);
|
||||
editSiteForm.auth = delta.auth.unwrap();
|
||||
await delta.client.editSite(editSiteForm);
|
||||
|
||||
editSiteForm.auth = epsilon.auth.unwrap();
|
||||
editSiteForm.allowed_instances = Some([]);
|
||||
editSiteForm.blocked_instances = Some(["lemmy-alpha"]);
|
||||
await epsilon.client.editSite(editSiteForm);
|
||||
|
||||
// Create the main alpha/beta communities
|
||||
|
@ -185,7 +238,7 @@ export async function createPost(
|
|||
): Promise<PostResponse> {
|
||||
let name = randomString(5);
|
||||
let body = Some(randomString(10));
|
||||
let url = Some('https://google.com/');
|
||||
let url = Some("https://google.com/");
|
||||
let form = new CreatePost({
|
||||
name,
|
||||
url,
|
||||
|
@ -194,12 +247,13 @@ export async function createPost(
|
|||
community_id,
|
||||
nsfw: None,
|
||||
honeypot: None,
|
||||
language_id: None,
|
||||
});
|
||||
return api.client.createPost(form);
|
||||
}
|
||||
|
||||
export async function editPost(api: API, post: Post): Promise<PostResponse> {
|
||||
let name = Some('A jest test federated post, updated');
|
||||
let name = Some("A jest test federated post, updated");
|
||||
let form = new EditPost({
|
||||
name,
|
||||
post_id: post.id,
|
||||
|
@ -207,6 +261,7 @@ export async function editPost(api: API, post: Post): Promise<PostResponse> {
|
|||
nsfw: None,
|
||||
url: None,
|
||||
body: None,
|
||||
language_id: None,
|
||||
});
|
||||
return api.client.editPost(form);
|
||||
}
|
||||
|
@ -342,7 +397,7 @@ export async function resolveBetaCommunity(
|
|||
): Promise<ResolveObjectResponse> {
|
||||
// Use short-hand search url
|
||||
let form = new ResolveObject({
|
||||
q: '!main@lemmy-beta:8551',
|
||||
q: "!main@lemmy-beta:8551",
|
||||
auth: api.auth,
|
||||
});
|
||||
return api.client.resolveObject(form);
|
||||
|
@ -415,7 +470,7 @@ export async function followCommunity(
|
|||
let form = new FollowCommunity({
|
||||
community_id,
|
||||
follow,
|
||||
auth: api.auth.unwrap()
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
return api.client.followCommunity(form);
|
||||
}
|
||||
|
@ -428,7 +483,7 @@ export async function likePost(
|
|||
let form = new CreatePostLike({
|
||||
post_id: post.id,
|
||||
score: score,
|
||||
auth: api.auth.unwrap()
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
|
||||
return api.client.likePost(form);
|
||||
|
@ -438,13 +493,14 @@ export async function createComment(
|
|||
api: API,
|
||||
post_id: number,
|
||||
parent_id: Option<number>,
|
||||
content = 'a jest test comment'
|
||||
content = "a jest test comment"
|
||||
): Promise<CommentResponse> {
|
||||
let form = new CreateComment({
|
||||
content,
|
||||
post_id,
|
||||
parent_id,
|
||||
form_id: None,
|
||||
language_id: None,
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
return api.client.createComment(form);
|
||||
|
@ -453,13 +509,15 @@ export async function createComment(
|
|||
export async function editComment(
|
||||
api: API,
|
||||
comment_id: number,
|
||||
content = 'A jest test federated comment update'
|
||||
content = Some("A jest test federated comment update")
|
||||
): Promise<CommentResponse> {
|
||||
let form = new EditComment({
|
||||
content,
|
||||
comment_id,
|
||||
form_id: None,
|
||||
auth: api.auth.unwrap()
|
||||
language_id: None,
|
||||
distinguished: None,
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
return api.client.editComment(form);
|
||||
}
|
||||
|
@ -491,7 +549,9 @@ export async function removeComment(
|
|||
return api.client.removeComment(form);
|
||||
}
|
||||
|
||||
export async function getMentions(api: API): Promise<GetPersonMentionsResponse> {
|
||||
export async function getMentions(
|
||||
api: API
|
||||
): Promise<GetPersonMentionsResponse> {
|
||||
let form = new GetPersonMentions({
|
||||
sort: Some(CommentSortType.New),
|
||||
unread_only: Some(false),
|
||||
|
@ -519,7 +579,7 @@ export async function createCommunity(
|
|||
api: API,
|
||||
name_: string = randomString(5)
|
||||
): Promise<CommunityResponse> {
|
||||
let description = Some('a sample description');
|
||||
let description = Some("a sample description");
|
||||
let form = new CreateCommunity({
|
||||
name: name_,
|
||||
title: name_,
|
||||
|
@ -577,7 +637,7 @@ export async function createPrivateMessage(
|
|||
api: API,
|
||||
recipient_id: number
|
||||
): Promise<PrivateMessageResponse> {
|
||||
let content = 'A jest test federated private message';
|
||||
let content = "A jest test federated private message";
|
||||
let form = new CreatePrivateMessage({
|
||||
content,
|
||||
recipient_id,
|
||||
|
@ -590,7 +650,7 @@ export async function editPrivateMessage(
|
|||
api: API,
|
||||
private_message_id: number
|
||||
): Promise<PrivateMessageResponse> {
|
||||
let updatedContent = 'A jest test federated private message edited';
|
||||
let updatedContent = "A jest test federated private message edited";
|
||||
let form = new EditPrivateMessage({
|
||||
content: updatedContent,
|
||||
private_message_id,
|
||||
|
@ -630,18 +690,18 @@ export async function registerUser(
|
|||
return api.client.register(form);
|
||||
}
|
||||
|
||||
export async function saveUserSettingsBio(
|
||||
api: API
|
||||
): Promise<LoginResponse> {
|
||||
export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
|
||||
let form = new SaveUserSettings({
|
||||
show_nsfw: Some(true),
|
||||
theme: Some('darkly'),
|
||||
theme: Some("darkly"),
|
||||
default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Active)),
|
||||
default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)),
|
||||
lang: Some('en'),
|
||||
default_listing_type: Some(
|
||||
Object.keys(ListingType).indexOf(ListingType.All)
|
||||
),
|
||||
interface_language: Some("en"),
|
||||
show_avatars: Some(true),
|
||||
send_notifications_to_email: Some(false),
|
||||
bio: Some('a changed bio'),
|
||||
bio: Some("a changed bio"),
|
||||
avatar: None,
|
||||
banner: None,
|
||||
display_name: None,
|
||||
|
@ -652,6 +712,7 @@ export async function saveUserSettingsBio(
|
|||
show_bot_accounts: None,
|
||||
show_new_post_notifs: None,
|
||||
bot_account: None,
|
||||
discussion_languages: None,
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
return saveUserSettings(api, form);
|
||||
|
@ -660,18 +721,20 @@ export async function saveUserSettingsBio(
|
|||
export async function saveUserSettingsFederated(
|
||||
api: API
|
||||
): Promise<LoginResponse> {
|
||||
let avatar = Some('https://image.flaticon.com/icons/png/512/35/35896.png');
|
||||
let banner = Some('https://image.flaticon.com/icons/png/512/36/35896.png');
|
||||
let bio = Some('a changed bio');
|
||||
let avatar = Some("https://image.flaticon.com/icons/png/512/35/35896.png");
|
||||
let banner = Some("https://image.flaticon.com/icons/png/512/36/35896.png");
|
||||
let bio = Some("a changed bio");
|
||||
let form = new SaveUserSettings({
|
||||
show_nsfw: Some(false),
|
||||
theme: Some(''),
|
||||
theme: Some(""),
|
||||
default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Hot)),
|
||||
default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)),
|
||||
lang: Some(''),
|
||||
default_listing_type: Some(
|
||||
Object.keys(ListingType).indexOf(ListingType.All)
|
||||
),
|
||||
interface_language: Some(""),
|
||||
avatar,
|
||||
banner,
|
||||
display_name: Some('user321'),
|
||||
display_name: Some("user321"),
|
||||
show_avatars: Some(false),
|
||||
send_notifications_to_email: Some(false),
|
||||
bio,
|
||||
|
@ -682,6 +745,7 @@ export async function saveUserSettingsFederated(
|
|||
bot_account: None,
|
||||
show_bot_accounts: None,
|
||||
show_new_post_notifs: None,
|
||||
discussion_languages: None,
|
||||
auth: api.auth.unwrap(),
|
||||
});
|
||||
return await saveUserSettings(alpha, form);
|
||||
|
@ -694,19 +758,15 @@ export async function saveUserSettings(
|
|||
return api.client.saveUserSettings(form);
|
||||
}
|
||||
|
||||
export async function deleteUser(
|
||||
api: API
|
||||
): Promise<DeleteAccountResponse> {
|
||||
export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
|
||||
let form = new DeleteAccount({
|
||||
auth: api.auth.unwrap(),
|
||||
password
|
||||
password,
|
||||
});
|
||||
return api.client.deleteAccount(form);
|
||||
}
|
||||
|
||||
export async function getSite(
|
||||
api: API
|
||||
): Promise<GetSiteResponse> {
|
||||
export async function getSite(api: API): Promise<GetSiteResponse> {
|
||||
let form = new GetSite({
|
||||
auth: api.auth,
|
||||
});
|
||||
|
@ -725,14 +785,12 @@ export async function listPrivateMessages(
|
|||
return api.client.getPrivateMessages(form);
|
||||
}
|
||||
|
||||
export async function unfollowRemotes(
|
||||
api: API
|
||||
): Promise<GetSiteResponse> {
|
||||
export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
|
||||
// Unfollow all remote communities
|
||||
let site = await getSite(api);
|
||||
let remoteFollowed = site.my_user.unwrap().follows.filter(
|
||||
c => c.community.local == false
|
||||
);
|
||||
let remoteFollowed = site.my_user
|
||||
.unwrap()
|
||||
.follows.filter(c => c.community.local == false);
|
||||
for (let cu of remoteFollowed) {
|
||||
await followCommunity(api, false, cu.community.id);
|
||||
}
|
||||
|
@ -743,7 +801,11 @@ export async function unfollowRemotes(
|
|||
export async function followBeta(api: API): Promise<CommunityResponse> {
|
||||
let betaCommunity = (await resolveBetaCommunity(api)).community;
|
||||
if (betaCommunity.isSome()) {
|
||||
let follow = await followCommunity(api, true, betaCommunity.unwrap().community.id);
|
||||
let follow = await followCommunity(
|
||||
api,
|
||||
true,
|
||||
betaCommunity.unwrap().community.id
|
||||
);
|
||||
return follow;
|
||||
} else {
|
||||
return Promise.reject("no community worked");
|
||||
|
@ -763,7 +825,9 @@ export async function reportPost(
|
|||
return api.client.createPostReport(form);
|
||||
}
|
||||
|
||||
export async function listPostReports(api: API): Promise<ListPostReportsResponse> {
|
||||
export async function listPostReports(
|
||||
api: API
|
||||
): Promise<ListPostReportsResponse> {
|
||||
let form = new ListPostReports({
|
||||
auth: api.auth.unwrap(),
|
||||
page: None,
|
||||
|
@ -787,7 +851,9 @@ export async function reportComment(
|
|||
return api.client.createCommentReport(form);
|
||||
}
|
||||
|
||||
export async function listCommentReports(api: API): Promise<ListCommentReportsResponse> {
|
||||
export async function listCommentReports(
|
||||
api: API
|
||||
): Promise<ListCommentReportsResponse> {
|
||||
let form = new ListCommentReports({
|
||||
page: None,
|
||||
limit: None,
|
||||
|
@ -798,7 +864,7 @@ export async function listCommentReports(api: API): Promise<ListCommentReportsRe
|
|||
return api.client.listCommentReports(form);
|
||||
}
|
||||
|
||||
export function delay(millis: number = 500) {
|
||||
export function delay(millis = 500) {
|
||||
return new Promise(resolve => setTimeout(resolve, millis));
|
||||
}
|
||||
|
||||
|
@ -811,8 +877,9 @@ export function wrapper(form: any): string {
|
|||
}
|
||||
|
||||
export function randomString(length: number): string {
|
||||
var result = '';
|
||||
var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
|
||||
var result = "";
|
||||
var characters =
|
||||
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
|
||||
var charactersLength = characters.length;
|
||||
for (var i = 0; i < length; i++) {
|
||||
result += characters.charAt(Math.floor(Math.random() * charactersLength));
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
jest.setTimeout(120000);
|
||||
import {None} from '@sniptt/monads';
|
||||
import {
|
||||
PersonViewSafe,
|
||||
} from 'lemmy-js-client';
|
||||
import { None } from "@sniptt/monads";
|
||||
import { PersonViewSafe } from "lemmy-js-client";
|
||||
|
||||
import {
|
||||
alpha,
|
||||
|
@ -20,7 +18,7 @@ import {
|
|||
resolveComment,
|
||||
saveUserSettingsFederated,
|
||||
setupLogins,
|
||||
} from './shared';
|
||||
} from "./shared";
|
||||
|
||||
beforeAll(async () => {
|
||||
await setupLogins();
|
||||
|
@ -28,59 +26,82 @@ beforeAll(async () => {
|
|||
|
||||
let apShortname: string;
|
||||
|
||||
function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) {
|
||||
function assertUserFederation(
|
||||
userOne: PersonViewSafe,
|
||||
userTwo: PersonViewSafe
|
||||
) {
|
||||
expect(userOne.person.name).toBe(userTwo.person.name);
|
||||
expect(userOne.person.display_name.unwrapOr("none")).toBe(userTwo.person.display_name.unwrapOr("none"));
|
||||
expect(userOne.person.bio.unwrapOr("none")).toBe(userTwo.person.bio.unwrapOr("none"));
|
||||
expect(userOne.person.display_name.unwrapOr("none")).toBe(
|
||||
userTwo.person.display_name.unwrapOr("none")
|
||||
);
|
||||
expect(userOne.person.bio.unwrapOr("none")).toBe(
|
||||
userTwo.person.bio.unwrapOr("none")
|
||||
);
|
||||
expect(userOne.person.actor_id).toBe(userTwo.person.actor_id);
|
||||
expect(userOne.person.avatar.unwrapOr("none")).toBe(userTwo.person.avatar.unwrapOr("none"));
|
||||
expect(userOne.person.banner.unwrapOr("none")).toBe(userTwo.person.banner.unwrapOr("none"));
|
||||
expect(userOne.person.avatar.unwrapOr("none")).toBe(
|
||||
userTwo.person.avatar.unwrapOr("none")
|
||||
);
|
||||
expect(userOne.person.banner.unwrapOr("none")).toBe(
|
||||
userTwo.person.banner.unwrapOr("none")
|
||||
);
|
||||
expect(userOne.person.published).toBe(userTwo.person.published);
|
||||
}
|
||||
|
||||
test('Create user', async () => {
|
||||
test("Create user", async () => {
|
||||
let userRes = await registerUser(alpha);
|
||||
expect(userRes.jwt).toBeDefined();
|
||||
alpha.auth = userRes.jwt;
|
||||
|
||||
let site = await getSite(alpha);
|
||||
expect(site.my_user).toBeDefined();
|
||||
apShortname = `@${site.my_user.unwrap().local_user_view.person.name}@lemmy-alpha:8541`;
|
||||
apShortname = `@${
|
||||
site.my_user.unwrap().local_user_view.person.name
|
||||
}@lemmy-alpha:8541`;
|
||||
});
|
||||
|
||||
test('Set some user settings, check that they are federated', async () => {
|
||||
test("Set some user settings, check that they are federated", async () => {
|
||||
await saveUserSettingsFederated(alpha);
|
||||
let alphaPerson = (await resolvePerson(alpha, apShortname)).person.unwrap();
|
||||
let betaPerson = (await resolvePerson(beta, apShortname)).person.unwrap();
|
||||
assertUserFederation(alphaPerson, betaPerson);
|
||||
});
|
||||
|
||||
test('Delete user', async () => {
|
||||
test("Delete user", async () => {
|
||||
let userRes = await registerUser(alpha);
|
||||
expect(userRes.jwt).toBeDefined();
|
||||
let user: API = {
|
||||
client: alpha.client,
|
||||
auth: userRes.jwt
|
||||
}
|
||||
auth: userRes.jwt,
|
||||
};
|
||||
|
||||
// make a local post and comment
|
||||
let alphaCommunity = (await resolveCommunity(user, '!main@lemmy-alpha:8541')).community.unwrap();
|
||||
let localPost = (await createPost(user, alphaCommunity.community.id)).post_view.post;
|
||||
let alphaCommunity = (
|
||||
await resolveCommunity(user, "!main@lemmy-alpha:8541")
|
||||
).community.unwrap();
|
||||
let localPost = (await createPost(user, alphaCommunity.community.id))
|
||||
.post_view.post;
|
||||
expect(localPost).toBeDefined();
|
||||
let localComment = (await createComment(user, localPost.id, None)).comment_view.comment;
|
||||
let localComment = (await createComment(user, localPost.id, None))
|
||||
.comment_view.comment;
|
||||
expect(localComment).toBeDefined();
|
||||
|
||||
// make a remote post and comment
|
||||
let betaCommunity = (await resolveBetaCommunity(user)).community.unwrap();
|
||||
let remotePost = (await createPost(user, betaCommunity.community.id)).post_view.post;
|
||||
let remotePost = (await createPost(user, betaCommunity.community.id))
|
||||
.post_view.post;
|
||||
expect(remotePost).toBeDefined();
|
||||
let remoteComment = (await createComment(user, remotePost.id, None)).comment_view.comment;
|
||||
let remoteComment = (await createComment(user, remotePost.id, None))
|
||||
.comment_view.comment;
|
||||
expect(remoteComment).toBeDefined();
|
||||
|
||||
await deleteUser(user);
|
||||
|
||||
expect((await resolvePost(alpha, localPost)).post.isNone()).toBe(true);
|
||||
expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe(true)
|
||||
expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true)
|
||||
expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe(true)
|
||||
expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe(
|
||||
true
|
||||
);
|
||||
expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true);
|
||||
expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
|
1428
api_tests/yarn.lock
1428
api_tests/yarn.lock
File diff suppressed because it is too large
Load diff
|
@ -2,11 +2,4 @@
|
|||
# https://join-lemmy.org/docs/en/administration/configuration.html
|
||||
{
|
||||
hostname: lemmy-alpha
|
||||
federation: {
|
||||
enabled: true
|
||||
}
|
||||
slur_filter:
|
||||
'''
|
||||
(fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?)
|
||||
'''
|
||||
}
|
||||
|
|
|
@ -14,64 +14,7 @@
|
|||
# Maximum number of active sql connections
|
||||
pool_size: 5
|
||||
}
|
||||
# rate limits for various user actions, by user ip
|
||||
rate_limit: {
|
||||
# Maximum number of messages created in interval
|
||||
message: 180
|
||||
# Interval length for message limit, in seconds
|
||||
message_per_second: 60
|
||||
# Maximum number of posts created in interval
|
||||
post: 6
|
||||
# Interval length for post limit, in seconds
|
||||
post_per_second: 600
|
||||
# Maximum number of registrations in interval
|
||||
register: 3
|
||||
# Interval length for registration limit, in seconds
|
||||
register_per_second: 3600
|
||||
# Maximum number of image uploads in interval
|
||||
image: 6
|
||||
# Interval length for image uploads, in seconds
|
||||
image_per_second: 3600
|
||||
# Maximum number of comments created in interval
|
||||
comment: 6
|
||||
# Interval length for comment limit, in seconds
|
||||
comment_per_second: 600
|
||||
search: 60
|
||||
# Interval length for search limit, in seconds
|
||||
search_per_second: 600
|
||||
}
|
||||
# Settings related to activitypub federation
|
||||
federation: {
|
||||
# Whether to enable activitypub federation.
|
||||
enabled: false
|
||||
# Allows and blocks are described here:
|
||||
# https://join-lemmy.org/docs/en/administration/federation_getting_started.html
|
||||
#
|
||||
# list of instances with which federation is allowed
|
||||
allowed_instances: [
|
||||
instance1.tld
|
||||
instance2.tld
|
||||
/* ... */
|
||||
]
|
||||
# Instances which we never federate anything with (but previously federated objects are unaffected)
|
||||
blocked_instances: [
|
||||
string
|
||||
/* ... */
|
||||
]
|
||||
# If true, only federate with instances on the allowlist and block everything else. If false
|
||||
# use allowlist only for remote communities, and posts/comments in local communities
|
||||
# (meaning remote communities will show content from arbitrary instances).
|
||||
strict_allowlist: true
|
||||
# Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object fetch through the search).
|
||||
http_fetch_retry_limit: 25
|
||||
# Number of workers for sending outgoing activities. Search logs for Activity queue stats to
|
||||
# see information. If running number is consistently close to the worker_count, you should
|
||||
# increase it.
|
||||
worker_count: 64
|
||||
# Use federation debug mode. Allows connecting to http and localhost urls. Also sends outgoing
|
||||
# activities synchronously for easier testing. Do not use in production.
|
||||
debug: false
|
||||
}
|
||||
# Pictrs image server configuration.
|
||||
pictrs: {
|
||||
# Address where pictrs is available (for image hosting)
|
||||
|
@ -79,12 +22,6 @@
|
|||
# Set a custom pictrs API key. ( Required for deleting images )
|
||||
api_key: "string"
|
||||
}
|
||||
captcha: {
|
||||
# Whether captcha is required for signup
|
||||
enabled: false
|
||||
# Can be easy, medium, or hard
|
||||
difficulty: "medium"
|
||||
}
|
||||
# Email sending configuration. All options except login/password are mandatory
|
||||
email: {
|
||||
# Hostname and port of the smtp server
|
||||
|
@ -117,8 +54,4 @@
|
|||
port: 8536
|
||||
# Whether the site is available over TLS. Needs to be true for federation to work.
|
||||
tls_enabled: true
|
||||
# A regex list of slurs to block / hide
|
||||
slur_filter: "(\bThis\b)|(\bis\b)|(\bsample\b)"
|
||||
# Maximum length of local community and user names
|
||||
actor_name_max_length: 20
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
comment::{CommentLike, CommentLikeForm},
|
||||
comment_reply::CommentReply,
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Likeable,
|
||||
};
|
||||
|
@ -35,13 +36,14 @@ impl Perform for CreateCommentLike {
|
|||
websocket_id: Option<ConnectionId>,
|
||||
) -> Result<CommentResponse, LemmyError> {
|
||||
let data: &CreateCommentLike = self;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
|
||||
let mut recipient_ids = Vec::<LocalUserId>::new();
|
||||
|
||||
// Don't do a downvote if site has downvotes disabled
|
||||
check_downvotes_enabled(data.score, context.pool()).await?;
|
||||
check_downvotes_enabled(data.score, &local_site)?;
|
||||
|
||||
let comment_id = data.comment_id;
|
||||
let orig_comment = blocking(context.pool(), move |conn| {
|
||||
|
|
|
@ -7,7 +7,10 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::protocol::activities::community::report::Report;
|
||||
use lemmy_db_schema::{
|
||||
source::comment_report::{CommentReport, CommentReportForm},
|
||||
source::{
|
||||
comment_report::{CommentReport, CommentReportForm},
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Reportable,
|
||||
};
|
||||
use lemmy_db_views::structs::{CommentReportView, CommentView};
|
||||
|
@ -28,9 +31,10 @@ impl Perform for CreateCommentReport {
|
|||
let data: &CreateCommentReport = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let reason = self.reason.trim();
|
||||
check_report_reason(reason, context)?;
|
||||
check_report_reason(reason, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let comment_id = data.comment_id;
|
||||
|
|
|
@ -7,11 +7,10 @@ use lemmy_api_common::{
|
|||
use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{Community, CommunityForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{ModHideCommunity, ModHideCommunityForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
|
||||
|
@ -33,20 +32,9 @@ impl Perform for HideCommunity {
|
|||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let community_id = data.community_id;
|
||||
let read_community = blocking(context.pool(), move |conn| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let community_form = CommunityForm {
|
||||
name: read_community.name,
|
||||
title: read_community.title,
|
||||
description: Some(read_community.description.to_owned()),
|
||||
hidden: Some(data.hidden),
|
||||
updated: Some(naive_now()),
|
||||
..CommunityForm::default()
|
||||
};
|
||||
let community_form = CommunityUpdateForm::builder()
|
||||
.hidden(Some(data.hidden))
|
||||
.build();
|
||||
|
||||
let mod_hide_community_form = ModHideCommunityForm {
|
||||
community_id: data.community_id,
|
||||
|
|
|
@ -7,8 +7,10 @@ use lemmy_api_common::{
|
|||
post::*,
|
||||
private_message::*,
|
||||
site::*,
|
||||
utils::local_site_to_slur_regex,
|
||||
websocket::*,
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
|
||||
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
|
||||
use serde::Deserialize;
|
||||
|
@ -227,8 +229,10 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
|
|||
}
|
||||
|
||||
/// Check size of report and remove whitespace
|
||||
pub(crate) fn check_report_reason(reason: &str, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
check_slurs(reason, &context.settings().slur_regex())?;
|
||||
pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
|
||||
let slur_regex = &local_site_to_slur_regex(local_site);
|
||||
|
||||
check_slurs(reason, slur_regex)?;
|
||||
if reason.is_empty() {
|
||||
return Err(LemmyError::from_message("report_reason_required"));
|
||||
}
|
||||
|
@ -243,8 +247,9 @@ mod tests {
|
|||
use lemmy_api_common::utils::check_validator_time;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
person::{Person, PersonForm},
|
||||
instance::Instance,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
secret::Secret,
|
||||
},
|
||||
traits::Crud,
|
||||
|
@ -258,19 +263,20 @@ mod tests {
|
|||
let secret = Secret::init(conn).unwrap();
|
||||
let settings = &SETTINGS.to_owned();
|
||||
|
||||
let new_person = PersonForm {
|
||||
name: "Gerry9812".into(),
|
||||
public_key: Some("pubkey".to_string()),
|
||||
..PersonForm::default()
|
||||
};
|
||||
let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap();
|
||||
|
||||
let new_person = PersonInsertForm::builder()
|
||||
.name("Gerry9812".into())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(inserted_instance.id)
|
||||
.build();
|
||||
|
||||
let inserted_person = Person::create(conn, &new_person).unwrap();
|
||||
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id: Some(inserted_person.id),
|
||||
password_encrypted: Some("123456".to_string()),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
let local_user_form = LocalUserInsertForm::builder()
|
||||
.person_id(inserted_person.id)
|
||||
.password_encrypted("123456".to_string())
|
||||
.build();
|
||||
|
||||
let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap();
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModAdd, ModAddForm},
|
||||
person::Person,
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -35,7 +35,11 @@ impl Perform for AddAdmin {
|
|||
let added = data.added;
|
||||
let added_person_id = data.person_id;
|
||||
let added_admin = blocking(context.pool(), move |conn| {
|
||||
Person::add_admin(conn, added_person_id, added)
|
||||
Person::update(
|
||||
conn,
|
||||
added_person_id,
|
||||
&PersonUpdateForm::builder().admin(Some(added)).build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
|
||||
|
|
|
@ -11,11 +11,11 @@ use lemmy_apub::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModBan, ModBanForm},
|
||||
person::Person,
|
||||
site::Site,
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_db_views_actor::structs::PersonViewSafe;
|
||||
use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
|
||||
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
|
||||
|
@ -41,8 +41,16 @@ impl Perform for BanPerson {
|
|||
let banned_person_id = data.person_id;
|
||||
let expires = data.expires.map(naive_from_unix);
|
||||
|
||||
let ban_person = move |conn: &mut _| Person::ban_person(conn, banned_person_id, ban, expires);
|
||||
let person = blocking(context.pool(), ban_person)
|
||||
let person = blocking(context.pool(), move |conn| {
|
||||
Person::update(
|
||||
conn,
|
||||
banned_person_id,
|
||||
&PersonUpdateForm::builder()
|
||||
.banned(Some(ban))
|
||||
.ban_expires(Some(expires))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;
|
||||
|
||||
|
@ -75,7 +83,12 @@ impl Perform for BanPerson {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let site = SiteOrCommunity::Site(blocking(context.pool(), Site::read_local).await??.into());
|
||||
let site = SiteOrCommunity::Site(
|
||||
blocking(context.pool(), SiteView::read_local)
|
||||
.await??
|
||||
.site
|
||||
.into(),
|
||||
);
|
||||
// if the action affects a local user, federate to other instances
|
||||
if person.local {
|
||||
if ban {
|
||||
|
|
|
@ -2,8 +2,11 @@ use crate::{captcha_as_wav_base64, Perform};
|
|||
use actix_web::web::Data;
|
||||
use captcha::{gen, Difficulty};
|
||||
use chrono::Duration;
|
||||
use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse};
|
||||
use lemmy_db_schema::utils::naive_now;
|
||||
use lemmy_api_common::{
|
||||
person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse},
|
||||
utils::blocking,
|
||||
};
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, utils::naive_now};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
|
||||
|
||||
|
@ -17,13 +20,13 @@ impl Perform for GetCaptcha {
|
|||
context: &Data<LemmyContext>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<Self::Response, LemmyError> {
|
||||
let captcha_settings = &context.settings().captcha;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
if !captcha_settings.enabled {
|
||||
if !local_site.captcha_enabled {
|
||||
return Ok(GetCaptchaResponse { ok: None });
|
||||
}
|
||||
|
||||
let captcha = gen(match captcha_settings.difficulty.as_str() {
|
||||
let captcha = gen(match local_site.captcha_difficulty.as_str() {
|
||||
"easy" => Difficulty::Easy,
|
||||
"hard" => Difficulty::Hard,
|
||||
_ => Difficulty::Medium,
|
||||
|
|
|
@ -5,7 +5,7 @@ use lemmy_api_common::{
|
|||
person::{Login, LoginResponse},
|
||||
utils::{blocking, check_registration_application, check_user_valid},
|
||||
};
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -22,6 +22,8 @@ impl Perform for Login {
|
|||
) -> Result<LoginResponse, LemmyError> {
|
||||
let data: &Login = self;
|
||||
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Fetch that username / email
|
||||
let username_or_email = data.username_or_email.clone();
|
||||
let local_user_view = blocking(context.pool(), move |conn| {
|
||||
|
@ -45,12 +47,11 @@ impl Perform for Login {
|
|||
local_user_view.person.deleted,
|
||||
)?;
|
||||
|
||||
let site = blocking(context.pool(), Site::read_local).await??;
|
||||
if site.require_email_verification && !local_user_view.local_user.email_verified {
|
||||
if local_site.require_email_verification && !local_user_view.local_user.email_verified {
|
||||
return Err(LemmyError::from_message("email_not_verified"));
|
||||
}
|
||||
|
||||
check_registration_application(&site, &local_user_view, context.pool()).await?;
|
||||
check_registration_application(&local_user_view, &local_site, context.pool()).await?;
|
||||
|
||||
// Return the jwt
|
||||
Ok(LoginResponse {
|
||||
|
|
|
@ -4,7 +4,10 @@ use lemmy_api_common::{
|
|||
person::{MarkPersonMentionAsRead, PersonMentionResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_db_schema::{source::person_mention::PersonMention, traits::Crud};
|
||||
use lemmy_db_schema::{
|
||||
source::person_mention::{PersonMention, PersonMentionUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::PersonMentionView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -34,10 +37,10 @@ impl Perform for MarkPersonMentionAsRead {
|
|||
}
|
||||
|
||||
let person_mention_id = read_person_mention.id;
|
||||
let read = data.read;
|
||||
let update_mention =
|
||||
move |conn: &mut _| PersonMention::update_read(conn, person_mention_id, read);
|
||||
blocking(context.pool(), update_mention)
|
||||
let read = Some(data.read);
|
||||
blocking(context.pool(), move |conn| {
|
||||
PersonMention::update(conn, person_mention_id, &PersonMentionUpdateForm { read })
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
||||
|
|
|
@ -4,7 +4,10 @@ use lemmy_api_common::{
|
|||
person::{CommentReplyResponse, MarkCommentReplyAsRead},
|
||||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_db_schema::{source::comment_reply::CommentReply, traits::Crud};
|
||||
use lemmy_db_schema::{
|
||||
source::comment_reply::{CommentReply, CommentReplyUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommentReplyView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -34,9 +37,10 @@ impl Perform for MarkCommentReplyAsRead {
|
|||
}
|
||||
|
||||
let comment_reply_id = read_comment_reply.id;
|
||||
let read = data.read;
|
||||
let update_reply = move |conn: &mut _| CommentReply::update_read(conn, comment_reply_id, read);
|
||||
blocking(context.pool(), update_reply)
|
||||
let read = Some(data.read);
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommentReply::update(conn, comment_reply_id, &CommentReplyUpdateForm { read })
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
||||
|
|
|
@ -7,12 +7,12 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::LocalUserLanguage,
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
person::{Person, PersonForm},
|
||||
site::Site,
|
||||
local_site::LocalSite,
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url},
|
||||
};
|
||||
use lemmy_utils::{
|
||||
claims::Claims,
|
||||
|
@ -35,6 +35,7 @@ impl Perform for SaveUserSettings {
|
|||
let data: &SaveUserSettings = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
|
@ -56,8 +57,7 @@ impl Perform for SaveUserSettings {
|
|||
|
||||
// When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
|
||||
if let Some(email) = &email {
|
||||
let site_fut = blocking(context.pool(), Site::read_local);
|
||||
if email.is_none() && site_fut.await??.require_email_verification {
|
||||
if email.is_none() && local_site.require_email_verification {
|
||||
return Err(LemmyError::from_message("email_required"));
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ impl Perform for SaveUserSettings {
|
|||
if let Some(Some(display_name)) = &display_name {
|
||||
if !is_valid_display_name(
|
||||
display_name.trim(),
|
||||
context.settings().actor_name_max_length,
|
||||
local_site.actor_name_max_length as usize,
|
||||
) {
|
||||
return Err(LemmyError::from_message("invalid_username"));
|
||||
}
|
||||
|
@ -87,31 +87,15 @@ impl Perform for SaveUserSettings {
|
|||
let person_id = local_user_view.person.id;
|
||||
let default_listing_type = data.default_listing_type;
|
||||
let default_sort_type = data.default_sort_type;
|
||||
let password_encrypted = local_user_view.local_user.password_encrypted;
|
||||
let public_key = Some(local_user_view.person.public_key);
|
||||
|
||||
let person_form = PersonForm {
|
||||
name: local_user_view.person.name,
|
||||
avatar,
|
||||
banner,
|
||||
inbox_url: None,
|
||||
display_name,
|
||||
published: None,
|
||||
updated: Some(naive_now()),
|
||||
banned: None,
|
||||
deleted: None,
|
||||
actor_id: None,
|
||||
bio,
|
||||
local: None,
|
||||
admin: None,
|
||||
private_key: None,
|
||||
public_key,
|
||||
last_refreshed_at: None,
|
||||
shared_inbox_url: None,
|
||||
matrix_user_id,
|
||||
bot_account,
|
||||
ban_expires: None,
|
||||
};
|
||||
let person_form = PersonUpdateForm::builder()
|
||||
.display_name(display_name)
|
||||
.bio(bio)
|
||||
.matrix_user_id(matrix_user_id)
|
||||
.bot_account(bot_account)
|
||||
.avatar(avatar)
|
||||
.banner(banner)
|
||||
.build();
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
Person::update(conn, person_id, &person_form)
|
||||
|
@ -126,24 +110,20 @@ impl Perform for SaveUserSettings {
|
|||
.await??;
|
||||
}
|
||||
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id: Some(person_id),
|
||||
email,
|
||||
password_encrypted: Some(password_encrypted),
|
||||
show_nsfw: data.show_nsfw,
|
||||
show_bot_accounts: data.show_bot_accounts,
|
||||
show_scores: data.show_scores,
|
||||
theme: data.theme.to_owned(),
|
||||
default_sort_type,
|
||||
default_listing_type,
|
||||
interface_language: data.interface_language.to_owned(),
|
||||
show_avatars: data.show_avatars,
|
||||
show_read_posts: data.show_read_posts,
|
||||
show_new_post_notifs: data.show_new_post_notifs,
|
||||
send_notifications_to_email: data.send_notifications_to_email,
|
||||
email_verified: None,
|
||||
accepted_application: None,
|
||||
};
|
||||
let local_user_form = LocalUserUpdateForm::builder()
|
||||
.email(email)
|
||||
.show_avatars(data.show_avatars)
|
||||
.show_read_posts(data.show_read_posts)
|
||||
.show_new_post_notifs(data.show_new_post_notifs)
|
||||
.send_notifications_to_email(data.send_notifications_to_email)
|
||||
.show_nsfw(data.show_nsfw)
|
||||
.show_bot_accounts(data.show_bot_accounts)
|
||||
.show_scores(data.show_scores)
|
||||
.default_sort_type(default_sort_type)
|
||||
.default_listing_type(default_listing_type)
|
||||
.theme(data.theme.to_owned())
|
||||
.interface_language(data.interface_language.to_owned())
|
||||
.build();
|
||||
|
||||
let local_user_res = blocking(context.pool(), move |conn| {
|
||||
LocalUser::update(conn, local_user_id, &local_user_form)
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
email_verification::EmailVerification,
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -31,13 +31,12 @@ impl Perform for VerifyEmail {
|
|||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?;
|
||||
|
||||
let form = LocalUserForm {
|
||||
let form = LocalUserUpdateForm::builder()
|
||||
// necessary in case this is a new signup
|
||||
email_verified: Some(true),
|
||||
.email_verified(Some(true))
|
||||
// necessary in case email of an existing user was changed
|
||||
email: Some(Some(verification.email)),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
.email(Some(Some(verification.email)))
|
||||
.build();
|
||||
let local_user_id = verification.local_user_id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::update(conn, local_user_id, &form)
|
||||
|
|
|
@ -20,7 +20,10 @@ use lemmy_apub::{
|
|||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::post::{Post, PostLike, PostLikeForm},
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostLike, PostLikeForm},
|
||||
},
|
||||
traits::{Crud, Likeable},
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
@ -39,9 +42,10 @@ impl Perform for CreatePostLike {
|
|||
let data: &CreatePostLike = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Don't do a downvote if site has downvotes disabled
|
||||
check_downvotes_enabled(data.score, context.pool()).await?;
|
||||
check_downvotes_enabled(data.score, &local_site)?;
|
||||
|
||||
// Check for a community ban
|
||||
let post_id = data.post_id;
|
||||
|
|
|
@ -17,7 +17,7 @@ use lemmy_apub::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModLockPost, ModLockPostForm},
|
||||
post::Post,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -61,7 +61,11 @@ impl Perform for LockPost {
|
|||
let post_id = data.post_id;
|
||||
let locked = data.locked;
|
||||
let updated_post: ApubPost = blocking(context.pool(), move |conn| {
|
||||
Post::update_locked(conn, post_id, locked)
|
||||
Post::update(
|
||||
conn,
|
||||
post_id,
|
||||
&PostUpdateForm::builder().locked(Some(locked)).build(),
|
||||
)
|
||||
})
|
||||
.await??
|
||||
.into();
|
||||
|
|
|
@ -17,7 +17,7 @@ use lemmy_apub::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
moderator::{ModStickyPost, ModStickyPostForm},
|
||||
post::Post,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -61,7 +61,11 @@ impl Perform for StickyPost {
|
|||
let post_id = data.post_id;
|
||||
let stickied = data.stickied;
|
||||
let updated_post: ApubPost = blocking(context.pool(), move |conn| {
|
||||
Post::update_stickied(conn, post_id, stickied)
|
||||
Post::update(
|
||||
conn,
|
||||
post_id,
|
||||
&PostUpdateForm::builder().stickied(Some(stickied)).build(),
|
||||
)
|
||||
})
|
||||
.await??
|
||||
.into();
|
||||
|
|
|
@ -7,7 +7,10 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::protocol::activities::community::report::Report;
|
||||
use lemmy_db_schema::{
|
||||
source::post_report::{PostReport, PostReportForm},
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
post_report::{PostReport, PostReportForm},
|
||||
},
|
||||
traits::Reportable,
|
||||
};
|
||||
use lemmy_db_views::structs::{PostReportView, PostView};
|
||||
|
@ -28,9 +31,10 @@ impl Perform for CreatePostReport {
|
|||
let data: &CreatePostReport = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let reason = self.reason.trim();
|
||||
check_report_reason(reason, context)?;
|
||||
check_report_reason(reason, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let post_id = data.post_id;
|
||||
|
|
|
@ -4,7 +4,10 @@ use lemmy_api_common::{
|
|||
private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
|
||||
use lemmy_db_schema::{
|
||||
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation};
|
||||
|
||||
|
@ -36,7 +39,11 @@ impl Perform for MarkPrivateMessageAsRead {
|
|||
let private_message_id = data.private_message_id;
|
||||
let read = data.read;
|
||||
blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::update_read(conn, private_message_id, read)
|
||||
PrivateMessage::update(
|
||||
conn,
|
||||
private_message_id,
|
||||
&PrivateMessageUpdateForm::builder().read(Some(read)).build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
|
||||
|
|
|
@ -7,6 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
private_message::PrivateMessage,
|
||||
private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
|
||||
},
|
||||
|
@ -28,9 +29,10 @@ impl Perform for CreatePrivateMessageReport {
|
|||
) -> Result<Self::Response, LemmyError> {
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let reason = self.reason.trim();
|
||||
check_report_reason(reason, context)?;
|
||||
check_report_reason(reason, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.person.id;
|
||||
let private_message_id = self.private_message_id;
|
||||
|
|
|
@ -2,14 +2,14 @@ use crate::Perform;
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
site::{GetSiteResponse, LeaveAdmin},
|
||||
utils::{blocking, build_federated_instances, get_local_user_view_from_jwt, is_admin},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
language::Language,
|
||||
moderator::{ModAdd, ModAddForm},
|
||||
person::Person,
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -42,7 +42,11 @@ impl Perform for LeaveAdmin {
|
|||
|
||||
let person_id = local_user_view.person.id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
Person::leave_admin(conn, person_id)
|
||||
Person::update(
|
||||
conn,
|
||||
person_id,
|
||||
&PersonUpdateForm::builder().admin(Some(false)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -59,18 +63,16 @@ impl Perform for LeaveAdmin {
|
|||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
|
||||
|
||||
let federated_instances = build_federated_instances(context.pool(), context.settings()).await?;
|
||||
|
||||
let all_languages = blocking(context.pool(), Language::read_all).await??;
|
||||
let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??;
|
||||
|
||||
Ok(GetSiteResponse {
|
||||
site_view: Some(site_view),
|
||||
site_view,
|
||||
admins,
|
||||
online: 0,
|
||||
version: version::VERSION.to_string(),
|
||||
my_user: None,
|
||||
federated_instances,
|
||||
federated_instances: None,
|
||||
all_languages,
|
||||
discussion_languages,
|
||||
})
|
||||
|
|
|
@ -12,7 +12,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{CommunityId, PersonId},
|
||||
source::site::Site,
|
||||
source::local_site::LocalSite,
|
||||
ModlogActionType,
|
||||
};
|
||||
use lemmy_db_views_moderator::structs::{
|
||||
|
@ -52,13 +52,13 @@ impl Perform for GetModlog {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let type_ = data.type_.unwrap_or(All);
|
||||
let community_id = data.community_id;
|
||||
|
||||
let site = blocking(context.pool(), Site::read_local).await??;
|
||||
let (local_person_id, is_admin) = match local_user_view {
|
||||
Some(s) => (s.person.id, is_admin(&s).is_ok()),
|
||||
None => (PersonId(-1), false),
|
||||
|
@ -71,7 +71,7 @@ impl Perform for GetModlog {
|
|||
&& is_mod_or_admin(context.pool(), local_person_id, community_id_value)
|
||||
.await
|
||||
.is_ok();
|
||||
let hide_modlog_names = site.hide_modlog_mod_names && !is_mod_of_community && !is_admin;
|
||||
let hide_modlog_names = local_site.hide_modlog_mod_names && !is_mod_of_community && !is_admin;
|
||||
|
||||
let mod_person_id = if hide_modlog_names {
|
||||
None
|
||||
|
|
|
@ -6,8 +6,8 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationForm},
|
||||
local_user::{LocalUser, LocalUserUpdateForm},
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::diesel_option_overwrite,
|
||||
|
@ -36,10 +36,9 @@ impl Perform for ApproveRegistrationApplication {
|
|||
|
||||
// Update the registration with reason, admin_id
|
||||
let deny_reason = diesel_option_overwrite(&data.deny_reason);
|
||||
let app_form = RegistrationApplicationForm {
|
||||
admin_id: Some(local_user_view.person.id),
|
||||
let app_form = RegistrationApplicationUpdateForm {
|
||||
admin_id: Some(Some(local_user_view.person.id)),
|
||||
deny_reason,
|
||||
..RegistrationApplicationForm::default()
|
||||
};
|
||||
|
||||
let registration_application = blocking(context.pool(), move |conn| {
|
||||
|
@ -48,10 +47,9 @@ impl Perform for ApproveRegistrationApplication {
|
|||
.await??;
|
||||
|
||||
// Update the local_user row
|
||||
let local_user_form = LocalUserForm {
|
||||
accepted_application: Some(data.approve),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
let local_user_form = LocalUserUpdateForm::builder()
|
||||
.accepted_application(Some(data.approve))
|
||||
.build();
|
||||
|
||||
let approved_user_id = registration_application.local_user_id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_api_common::{
|
|||
site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::registration_application_view::RegistrationApplicationQuery;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -22,14 +22,13 @@ impl Perform for ListRegistrationApplications {
|
|||
let data = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let unread_only = data.unread_only;
|
||||
let verified_email_only = blocking(context.pool(), Site::read_local)
|
||||
.await??
|
||||
.require_email_verification;
|
||||
let verified_email_only = local_site.require_email_verification;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_api_common::{
|
|||
site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin},
|
||||
};
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::RegistrationApplicationView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -21,13 +21,12 @@ impl Perform for GetUnreadRegistrationApplicationCount {
|
|||
let data = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Only let admins do this
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let verified_email_only = blocking(context.pool(), Site::read_local)
|
||||
.await??
|
||||
.require_email_verification;
|
||||
let verified_email_only = local_site.require_email_verification;
|
||||
|
||||
let registration_applications = blocking(context.pool(), move |conn| {
|
||||
RegistrationApplicationView::get_unread_count(conn, verified_email_only)
|
||||
|
|
|
@ -6,7 +6,7 @@ use lemmy_api_common::{
|
|||
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_apub::fetcher::search::{search_query_to_object_id, SearchableObjects};
|
||||
use lemmy_db_schema::{newtypes::PersonId, utils::DbPool};
|
||||
use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool};
|
||||
use lemmy_db_views::structs::{CommentView, PostView};
|
||||
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
@ -25,7 +25,8 @@ impl Perform for ResolveObject {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let res = search_query_to_object_id(&self.q, local_user_view.is_none(), context)
|
||||
.await
|
||||
|
|
|
@ -6,7 +6,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
|
||||
use lemmy_db_schema::{
|
||||
source::community::Community,
|
||||
source::{community::Community, local_site::LocalSite},
|
||||
traits::DeleteableOrRemoveable,
|
||||
utils::post_to_comment_sort_type,
|
||||
SearchType,
|
||||
|
@ -31,7 +31,9 @@ impl Perform for Search {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.as_ref().map(|u| u.person.id);
|
||||
let local_user = local_user_view.map(|l| l.local_user);
|
||||
|
|
|
@ -35,6 +35,7 @@ percent-encoding = { version = "2.2.0", optional = true }
|
|||
encoding = { version = "0.2.33", optional = true }
|
||||
reqwest-middleware = { version = "0.1.6", optional = true }
|
||||
webpage = { version = "1.4.0", default-features = false, features = ["serde"], optional = true }
|
||||
regex = "1.6.0"
|
||||
|
||||
[dev-dependencies]
|
||||
actix-rt = { version = "2.7.0", default-features = false }
|
||||
|
|
|
@ -128,6 +128,30 @@ pub struct CreateSite {
|
|||
pub application_email_admins: Option<bool>,
|
||||
pub auth: Sensitive<String>,
|
||||
pub hide_modlog_mod_names: Option<bool>,
|
||||
pub legal_information: Option<String>,
|
||||
pub slur_filter_regex: Option<String>,
|
||||
pub actor_name_max_length: Option<i32>,
|
||||
pub rate_limit_message: Option<i32>,
|
||||
pub rate_limit_message_per_second: Option<i32>,
|
||||
pub rate_limit_post: Option<i32>,
|
||||
pub rate_limit_post_per_second: Option<i32>,
|
||||
pub rate_limit_register: Option<i32>,
|
||||
pub rate_limit_register_per_second: Option<i32>,
|
||||
pub rate_limit_image: Option<i32>,
|
||||
pub rate_limit_image_per_second: Option<i32>,
|
||||
pub rate_limit_comment: Option<i32>,
|
||||
pub rate_limit_comment_per_second: Option<i32>,
|
||||
pub rate_limit_search: Option<i32>,
|
||||
pub rate_limit_search_per_second: Option<i32>,
|
||||
pub federation_enabled: Option<bool>,
|
||||
pub federation_debug: Option<bool>,
|
||||
pub federation_strict_allowlist: Option<bool>,
|
||||
pub federation_http_fetch_retry_limit: Option<i32>,
|
||||
pub federation_worker_count: Option<i32>,
|
||||
pub captcha_enabled: Option<bool>,
|
||||
pub captcha_difficulty: Option<String>,
|
||||
pub allowed_instances: Option<Vec<String>>,
|
||||
pub blocked_instances: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
|
||||
|
@ -151,6 +175,29 @@ pub struct EditSite {
|
|||
pub application_email_admins: Option<bool>,
|
||||
pub hide_modlog_mod_names: Option<bool>,
|
||||
pub discussion_languages: Option<Vec<LanguageId>>,
|
||||
pub slur_filter_regex: Option<String>,
|
||||
pub actor_name_max_length: Option<i32>,
|
||||
pub rate_limit_message: Option<i32>,
|
||||
pub rate_limit_message_per_second: Option<i32>,
|
||||
pub rate_limit_post: Option<i32>,
|
||||
pub rate_limit_post_per_second: Option<i32>,
|
||||
pub rate_limit_register: Option<i32>,
|
||||
pub rate_limit_register_per_second: Option<i32>,
|
||||
pub rate_limit_image: Option<i32>,
|
||||
pub rate_limit_image_per_second: Option<i32>,
|
||||
pub rate_limit_comment: Option<i32>,
|
||||
pub rate_limit_comment_per_second: Option<i32>,
|
||||
pub rate_limit_search: Option<i32>,
|
||||
pub rate_limit_search_per_second: Option<i32>,
|
||||
pub federation_enabled: Option<bool>,
|
||||
pub federation_debug: Option<bool>,
|
||||
pub federation_strict_allowlist: Option<bool>,
|
||||
pub federation_http_fetch_retry_limit: Option<i32>,
|
||||
pub federation_worker_count: Option<i32>,
|
||||
pub captcha_enabled: Option<bool>,
|
||||
pub captcha_difficulty: Option<String>,
|
||||
pub allowed_instances: Option<Vec<String>>,
|
||||
pub blocked_instances: Option<Vec<String>>,
|
||||
pub auth: Sensitive<String>,
|
||||
}
|
||||
|
||||
|
@ -166,7 +213,7 @@ pub struct SiteResponse {
|
|||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct GetSiteResponse {
|
||||
pub site_view: Option<SiteView>, // Because the site might not be set up yet
|
||||
pub site_view: SiteView,
|
||||
pub admins: Vec<PersonViewSafe>,
|
||||
pub online: usize,
|
||||
pub version: String,
|
||||
|
|
|
@ -4,16 +4,18 @@ use lemmy_db_schema::{
|
|||
impls::person::is_banned,
|
||||
newtypes::{CommunityId, LocalUserId, PersonId, PostId},
|
||||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
email_verification::{EmailVerification, EmailVerificationForm},
|
||||
instance::Instance,
|
||||
local_site::LocalSite,
|
||||
local_site_rate_limit::LocalSiteRateLimit,
|
||||
password_reset_request::PasswordResetRequest,
|
||||
person::Person,
|
||||
person::{Person, PersonUpdateForm},
|
||||
person_block::PersonBlock,
|
||||
post::{Post, PostRead, PostReadForm},
|
||||
registration_application::RegistrationApplication,
|
||||
secret::Secret,
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, Readable},
|
||||
utils::DbPool,
|
||||
|
@ -32,9 +34,11 @@ use lemmy_utils::{
|
|||
claims::Claims,
|
||||
email::{send_email, translations::Lang},
|
||||
error::LemmyError,
|
||||
rate_limit::RateLimitConfig,
|
||||
settings::structs::Settings,
|
||||
utils::generate_random_string,
|
||||
utils::{build_slur_regex, generate_random_string},
|
||||
};
|
||||
use regex::Regex;
|
||||
use reqwest_middleware::ClientWithMiddleware;
|
||||
use rosetta_i18n::{Language, LanguageId};
|
||||
use std::str::FromStr;
|
||||
|
@ -265,67 +269,38 @@ pub async fn check_person_block(
|
|||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> {
|
||||
if score == -1 {
|
||||
let site = blocking(pool, Site::read_local).await??;
|
||||
if !site.enable_downvotes {
|
||||
pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> {
|
||||
if score == -1 && !local_site.enable_downvotes {
|
||||
return Err(LemmyError::from_message("downvotes_disabled"));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn check_private_instance(
|
||||
pub fn check_private_instance(
|
||||
local_user_view: &Option<LocalUserView>,
|
||||
pool: &DbPool,
|
||||
local_site: &LocalSite,
|
||||
) -> Result<(), LemmyError> {
|
||||
if local_user_view.is_none() {
|
||||
let site = blocking(pool, Site::read_local).await?;
|
||||
|
||||
// The site might not be set up yet
|
||||
if let Ok(site) = site {
|
||||
if site.private_instance {
|
||||
if local_user_view.is_none() && local_site.private_instance {
|
||||
return Err(LemmyError::from_message("instance_is_private"));
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn build_federated_instances(
|
||||
local_site: &LocalSite,
|
||||
pool: &DbPool,
|
||||
settings: &Settings,
|
||||
) -> Result<Option<FederatedInstances>, LemmyError> {
|
||||
let federation_config = &settings.federation;
|
||||
let hostname = &settings.hostname;
|
||||
let federation = federation_config.to_owned();
|
||||
if federation.enabled {
|
||||
let distinct_communities = blocking(pool, move |conn| {
|
||||
Community::distinct_federated_communities(conn)
|
||||
})
|
||||
.await??;
|
||||
if local_site.federation_enabled {
|
||||
// TODO I hate that this requires 3 queries
|
||||
let linked = blocking(pool, Instance::linked).await??;
|
||||
let allowed = blocking(pool, Instance::allowlist).await??;
|
||||
let blocked = blocking(pool, Instance::blocklist).await??;
|
||||
|
||||
let allowed = federation.allowed_instances;
|
||||
let blocked = federation.blocked_instances;
|
||||
|
||||
let mut linked = distinct_communities
|
||||
.iter()
|
||||
.map(|actor_id| Ok(actor_id.host_str().unwrap_or("").to_string()))
|
||||
.collect::<Result<Vec<String>, LemmyError>>()?;
|
||||
|
||||
if let Some(allowed) = allowed.as_ref() {
|
||||
linked.extend_from_slice(allowed);
|
||||
}
|
||||
|
||||
if let Some(blocked) = blocked.as_ref() {
|
||||
linked.retain(|a| !blocked.contains(a) && !a.eq(hostname));
|
||||
}
|
||||
|
||||
// Sort and remove dupes
|
||||
linked.sort_unstable();
|
||||
linked.dedup();
|
||||
// These can return empty vectors, so convert them to options
|
||||
let allowed = (!allowed.is_empty()).then(|| allowed);
|
||||
let blocked = (!blocked.is_empty()).then(|| blocked);
|
||||
|
||||
Ok(Some(FederatedInstances {
|
||||
linked,
|
||||
|
@ -467,6 +442,37 @@ fn lang_str_to_lang(lang: &str) -> Lang {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn local_site_rate_limit_to_rate_limit_config(
|
||||
local_site_rate_limit: &LocalSiteRateLimit,
|
||||
) -> RateLimitConfig {
|
||||
let l = local_site_rate_limit;
|
||||
RateLimitConfig {
|
||||
message: l.message,
|
||||
message_per_second: l.message_per_second,
|
||||
post: l.post,
|
||||
post_per_second: l.post_per_second,
|
||||
register: l.register,
|
||||
register_per_second: l.register_per_second,
|
||||
image: l.image,
|
||||
image_per_second: l.image_per_second,
|
||||
comment: l.comment,
|
||||
comment_per_second: l.comment_per_second,
|
||||
search: l.search,
|
||||
search_per_second: l.search_per_second,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<Regex> {
|
||||
build_slur_regex(local_site.slur_filter_regex.as_deref())
|
||||
}
|
||||
|
||||
pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Regex> {
|
||||
local_site
|
||||
.as_ref()
|
||||
.map(local_site_to_slur_regex)
|
||||
.unwrap_or(None)
|
||||
}
|
||||
|
||||
pub fn send_application_approved_email(
|
||||
user: &LocalUserView,
|
||||
settings: &Settings,
|
||||
|
@ -506,11 +512,11 @@ pub async fn send_new_applicant_email_to_admins(
|
|||
}
|
||||
|
||||
pub async fn check_registration_application(
|
||||
site: &Site,
|
||||
local_user_view: &LocalUserView,
|
||||
local_site: &LocalSite,
|
||||
pool: &DbPool,
|
||||
) -> Result<(), LemmyError> {
|
||||
if site.require_application
|
||||
if local_site.require_application
|
||||
&& !local_user_view.local_user.accepted_application
|
||||
&& !local_user_view.person.admin
|
||||
{
|
||||
|
@ -531,20 +537,14 @@ pub async fn check_registration_application(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done.
|
||||
pub async fn check_private_instance_and_federation_enabled(
|
||||
pool: &DbPool,
|
||||
settings: &Settings,
|
||||
pub fn check_private_instance_and_federation_enabled(
|
||||
local_site: &LocalSite,
|
||||
) -> Result<(), LemmyError> {
|
||||
let site_opt = blocking(pool, Site::read_local).await?;
|
||||
|
||||
if let Ok(site) = site_opt {
|
||||
if site.private_instance && settings.federation.enabled {
|
||||
if local_site.private_instance && local_site.federation_enabled {
|
||||
return Err(LemmyError::from_message(
|
||||
"Cannot have both private instance and federation enabled.",
|
||||
));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -627,7 +627,14 @@ pub async fn remove_user_data(
|
|||
|
||||
// Update the fields to None
|
||||
blocking(pool, move |conn| {
|
||||
Person::remove_avatar_and_banner(conn, banned_person_id)
|
||||
Person::update(
|
||||
conn,
|
||||
banned_person_id,
|
||||
&PersonUpdateForm::builder()
|
||||
.avatar(Some(None))
|
||||
.banner(Some(None))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -656,8 +663,12 @@ pub async fn remove_user_data(
|
|||
|
||||
for first_mod_community in banned_user_first_communities {
|
||||
let community_id = first_mod_community.community.id;
|
||||
blocking(pool, move |conn: &mut _| {
|
||||
Community::update_removed(conn, community_id, true)
|
||||
blocking(pool, move |conn| {
|
||||
Community::update(
|
||||
conn,
|
||||
community_id,
|
||||
&CommunityUpdateForm::builder().removed(Some(true)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -672,7 +683,14 @@ pub async fn remove_user_data(
|
|||
}
|
||||
// Update the fields to None
|
||||
blocking(pool, move |conn| {
|
||||
Community::remove_avatar_and_banner(conn, community_id)
|
||||
Community::update(
|
||||
conn,
|
||||
community_id,
|
||||
&CommunityUpdateForm::builder()
|
||||
.icon(Some(None))
|
||||
.banner(Some(None))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
|
@ -713,7 +731,11 @@ pub async fn remove_user_data_in_community(
|
|||
for comment_view in &comments {
|
||||
let comment_id = comment_view.comment.id;
|
||||
blocking(pool, move |conn| {
|
||||
Comment::update_removed(conn, comment_id, true)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment_id,
|
||||
&CommentUpdateForm::builder().removed(Some(true)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
|
@ -761,15 +783,11 @@ pub async fn delete_user_account(
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn listing_type_with_site_default(
|
||||
pub fn listing_type_with_site_default(
|
||||
listing_type: Option<ListingType>,
|
||||
pool: &DbPool,
|
||||
local_site: &LocalSite,
|
||||
) -> Result<ListingType, LemmyError> {
|
||||
Ok(match listing_type {
|
||||
Some(l) => l,
|
||||
None => {
|
||||
let site = blocking(pool, Site::read_local).await??;
|
||||
ListingType::from_str(&site.default_post_listing_type)?
|
||||
}
|
||||
})
|
||||
Ok(listing_type.unwrap_or(ListingType::from_str(
|
||||
&local_site.default_post_listing_type,
|
||||
)?))
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ use lemmy_api_common::{
|
|||
check_post_deleted_or_removed,
|
||||
get_local_user_view_from_jwt,
|
||||
get_post,
|
||||
local_site_to_slur_regex,
|
||||
},
|
||||
};
|
||||
use lemmy_apub::{
|
||||
|
@ -20,9 +21,10 @@ use lemmy_apub::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
|
||||
comment_reply::CommentReply,
|
||||
person_mention::PersonMention,
|
||||
comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
|
||||
comment_reply::{CommentReply, CommentReplyUpdateForm},
|
||||
local_site::LocalSite,
|
||||
person_mention::{PersonMention, PersonMentionUpdateForm},
|
||||
},
|
||||
traits::{Crud, Likeable},
|
||||
};
|
||||
|
@ -50,9 +52,12 @@ impl PerformCrud for CreateComment {
|
|||
let data: &CreateComment = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let content_slurs_removed =
|
||||
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
|
||||
let content_slurs_removed = remove_slurs(
|
||||
&data.content.to_owned(),
|
||||
&local_site_to_slur_regex(&local_site),
|
||||
);
|
||||
|
||||
// Check for a community ban
|
||||
let post_id = data.post_id;
|
||||
|
@ -97,13 +102,12 @@ impl PerformCrud for CreateComment {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let comment_form = CommentForm {
|
||||
content: content_slurs_removed,
|
||||
post_id: data.post_id,
|
||||
creator_id: local_user_view.person.id,
|
||||
language_id: Some(language_id),
|
||||
..CommentForm::default()
|
||||
};
|
||||
let comment_form = CommentInsertForm::builder()
|
||||
.content(content_slurs_removed.to_owned())
|
||||
.post_id(data.post_id)
|
||||
.creator_id(local_user_view.person.id)
|
||||
.language_id(Some(language_id))
|
||||
.build();
|
||||
|
||||
// Create the comment
|
||||
let comment_form2 = comment_form.clone();
|
||||
|
@ -125,14 +129,18 @@ impl PerformCrud for CreateComment {
|
|||
&inserted_comment_id.to_string(),
|
||||
&protocol_and_hostname,
|
||||
)?;
|
||||
Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?)
|
||||
Ok(Comment::update(
|
||||
conn,
|
||||
inserted_comment_id,
|
||||
&CommentUpdateForm::builder().ap_id(Some(apub_id)).build(),
|
||||
)?)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| e.with_message("couldnt_create_comment"))?;
|
||||
|
||||
// Scan the comment for user mentions, add those rows
|
||||
let post_id = post.id;
|
||||
let mentions = scrape_text_for_mentions(&comment_form.content);
|
||||
let mentions = scrape_text_for_mentions(&content_slurs_removed);
|
||||
let recipient_ids = send_local_notifs(
|
||||
mentions,
|
||||
&updated_comment,
|
||||
|
@ -175,7 +183,7 @@ impl PerformCrud for CreateComment {
|
|||
.await?;
|
||||
if let Ok(reply) = comment_reply {
|
||||
blocking(context.pool(), move |conn| {
|
||||
CommentReply::update_read(conn, reply.id, true)
|
||||
CommentReply::update(conn, reply.id, &CommentReplyUpdateForm { read: Some(true) })
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_replies"))?;
|
||||
|
@ -189,7 +197,11 @@ impl PerformCrud for CreateComment {
|
|||
.await?;
|
||||
if let Ok(mention) = person_mention {
|
||||
blocking(context.pool(), move |conn| {
|
||||
PersonMention::update_read(conn, mention.id, true)
|
||||
PersonMention::update(
|
||||
conn,
|
||||
mention.id,
|
||||
&PersonMentionUpdateForm { read: Some(true) },
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_person_mentions"))?;
|
||||
|
|
|
@ -6,7 +6,11 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
|
||||
use lemmy_db_schema::{
|
||||
source::{comment::Comment, community::Community, post::Post},
|
||||
source::{
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::Community,
|
||||
post::Post,
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::CommentView;
|
||||
|
@ -57,7 +61,11 @@ impl PerformCrud for DeleteComment {
|
|||
// Do the delete
|
||||
let deleted = data.deleted;
|
||||
let updated_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_deleted(conn, comment_id, deleted)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment_id,
|
||||
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
|
|
@ -11,7 +11,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
|
||||
use lemmy_db_schema::{
|
||||
source::{comment::Comment, community::Community},
|
||||
source::{comment::Comment, community::Community, local_site::LocalSite},
|
||||
traits::{Crud, DeleteableOrRemoveable},
|
||||
};
|
||||
use lemmy_db_views::comment_view::CommentQuery;
|
||||
|
@ -32,10 +32,11 @@ impl PerformCrud for GetComments {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let community_id = data.community_id;
|
||||
let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?;
|
||||
let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
|
||||
|
||||
let community_actor_id = if let Some(name) = &data.community_name {
|
||||
resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
|
||||
|
|
|
@ -4,6 +4,7 @@ use lemmy_api_common::{
|
|||
comment::{CommentResponse, GetComment},
|
||||
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_db_schema::source::local_site::LocalSite;
|
||||
use lemmy_db_views::structs::CommentView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -22,8 +23,9 @@ impl PerformCrud for GetComment {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
let id = data.id;
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::Comment,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::Community,
|
||||
moderator::{ModRemoveComment, ModRemoveCommentForm},
|
||||
post::Post,
|
||||
|
@ -60,7 +60,11 @@ impl PerformCrud for RemoveComment {
|
|||
// Do the remove
|
||||
let removed = data.removed;
|
||||
let updated_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_removed(conn, comment_id, removed)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment_id,
|
||||
&CommentUpdateForm::builder().removed(Some(removed)).build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
|
||||
|
|
|
@ -8,6 +8,7 @@ use lemmy_api_common::{
|
|||
check_post_deleted_or_removed,
|
||||
get_local_user_view_from_jwt,
|
||||
is_mod_or_admin,
|
||||
local_site_to_slur_regex,
|
||||
},
|
||||
};
|
||||
use lemmy_apub::protocol::activities::{
|
||||
|
@ -17,7 +18,8 @@ use lemmy_apub::protocol::activities::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
comment::{Comment, CommentForm},
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -48,6 +50,7 @@ impl PerformCrud for EditComment {
|
|||
let data: &EditComment = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let comment_id = data.comment_id;
|
||||
let orig_comment = blocking(context.pool(), move |conn| {
|
||||
|
@ -90,16 +93,13 @@ impl PerformCrud for EditComment {
|
|||
let content_slurs_removed = data
|
||||
.content
|
||||
.as_ref()
|
||||
.map(|c| remove_slurs(c, &context.settings().slur_regex()));
|
||||
.map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site)));
|
||||
let comment_id = data.comment_id;
|
||||
let form = CommentForm {
|
||||
creator_id: orig_comment.comment.creator_id,
|
||||
post_id: orig_comment.comment.post_id,
|
||||
content: content_slurs_removed.unwrap_or(orig_comment.comment.content),
|
||||
distinguished: data.distinguished,
|
||||
language_id: data.language_id,
|
||||
..Default::default()
|
||||
};
|
||||
let form = CommentUpdateForm::builder()
|
||||
.content(content_slurs_removed)
|
||||
.distinguished(data.distinguished)
|
||||
.language_id(data.language_id)
|
||||
.build();
|
||||
let updated_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update(conn, comment_id, &form)
|
||||
})
|
||||
|
|
|
@ -3,7 +3,7 @@ use activitypub_federation::core::{object_id::ObjectId, signatures::generate_act
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
community::{CommunityResponse, CreateCommunity},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin, local_site_to_slur_regex},
|
||||
};
|
||||
use lemmy_apub::{
|
||||
generate_followers_url,
|
||||
|
@ -14,20 +14,18 @@ use lemmy_apub::{
|
|||
EndpointType,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::{
|
||||
source::community::{
|
||||
Community,
|
||||
CommunityFollower,
|
||||
CommunityFollowerForm,
|
||||
CommunityForm,
|
||||
CommunityInsertForm,
|
||||
CommunityModerator,
|
||||
CommunityModeratorForm,
|
||||
},
|
||||
site::Site,
|
||||
},
|
||||
traits::{Crud, Followable, Joinable},
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url},
|
||||
utils::diesel_option_overwrite_to_url_create,
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
|
@ -49,8 +47,9 @@ impl PerformCrud for CreateCommunity {
|
|||
let data: &CreateCommunity = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
let local_site = site_view.local_site;
|
||||
|
||||
let local_site = blocking(context.pool(), Site::read_local).await??;
|
||||
if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
|
||||
return Err(LemmyError::from_message(
|
||||
"only_admins_can_create_communities",
|
||||
|
@ -58,15 +57,15 @@ impl PerformCrud for CreateCommunity {
|
|||
}
|
||||
|
||||
// Check to make sure the icon and banners are urls
|
||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
let description = diesel_option_overwrite(&data.description);
|
||||
let icon = diesel_option_overwrite_to_url_create(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url_create(&data.banner)?;
|
||||
|
||||
check_slurs(&data.name, &context.settings().slur_regex())?;
|
||||
check_slurs(&data.title, &context.settings().slur_regex())?;
|
||||
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs(&data.name, &slur_regex)?;
|
||||
check_slurs(&data.title, &slur_regex)?;
|
||||
check_slurs_opt(&data.description, &slur_regex)?;
|
||||
|
||||
if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) {
|
||||
if !is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize) {
|
||||
return Err(LemmyError::from_message("invalid_community_name"));
|
||||
}
|
||||
|
||||
|
@ -85,22 +84,22 @@ impl PerformCrud for CreateCommunity {
|
|||
// When you create a community, make sure the user becomes a moderator and a follower
|
||||
let keypair = generate_actor_keypair()?;
|
||||
|
||||
let community_form = CommunityForm {
|
||||
name: data.name.to_owned(),
|
||||
title: data.title.to_owned(),
|
||||
description,
|
||||
icon,
|
||||
banner,
|
||||
nsfw: data.nsfw,
|
||||
actor_id: Some(community_actor_id.to_owned()),
|
||||
private_key: Some(Some(keypair.private_key)),
|
||||
public_key: Some(keypair.public_key),
|
||||
followers_url: Some(generate_followers_url(&community_actor_id)?),
|
||||
inbox_url: Some(generate_inbox_url(&community_actor_id)?),
|
||||
shared_inbox_url: Some(Some(generate_shared_inbox_url(&community_actor_id)?)),
|
||||
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
||||
..CommunityForm::default()
|
||||
};
|
||||
let community_form = CommunityInsertForm::builder()
|
||||
.name(data.name.to_owned())
|
||||
.title(data.title.to_owned())
|
||||
.description(data.description.to_owned())
|
||||
.icon(icon)
|
||||
.banner(banner)
|
||||
.nsfw(data.nsfw)
|
||||
.actor_id(Some(community_actor_id.to_owned()))
|
||||
.private_key(Some(keypair.private_key))
|
||||
.public_key(keypair.public_key)
|
||||
.followers_url(Some(generate_followers_url(&community_actor_id)?))
|
||||
.inbox_url(Some(generate_inbox_url(&community_actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?))
|
||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||
.instance_id(site_view.site.instance_id)
|
||||
.build();
|
||||
|
||||
let inserted_community = blocking(context.pool(), move |conn| {
|
||||
Community::create(conn, &community_form)
|
||||
|
|
|
@ -5,7 +5,10 @@ use lemmy_api_common::{
|
|||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
|
||||
use lemmy_db_schema::source::community::Community;
|
||||
use lemmy_db_schema::{
|
||||
source::community::{Community, CommunityUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
|
||||
|
@ -40,7 +43,13 @@ impl PerformCrud for DeleteCommunity {
|
|||
let community_id = data.community_id;
|
||||
let deleted = data.deleted;
|
||||
let updated_community = blocking(context.pool(), move |conn| {
|
||||
Community::update_deleted(conn, community_id, deleted)
|
||||
Community::update(
|
||||
conn,
|
||||
community_id,
|
||||
&CommunityUpdateForm::builder()
|
||||
.deleted(Some(deleted))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;
|
||||
|
|
|
@ -4,7 +4,7 @@ use lemmy_api_common::{
|
|||
community::{ListCommunities, ListCommunitiesResponse},
|
||||
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_db_schema::traits::DeleteableOrRemoveable;
|
||||
use lemmy_db_schema::{source::local_site::LocalSite, traits::DeleteableOrRemoveable};
|
||||
use lemmy_db_views_actor::community_view::CommunityQuery;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -23,8 +23,9 @@ impl PerformCrud for ListCommunities {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.to_owned().map(|l| l.person.id);
|
||||
|
||||
|
|
|
@ -10,7 +10,12 @@ use lemmy_apub::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
impls::actor_language::default_post_language,
|
||||
source::{actor_language::CommunityLanguage, community::Community, site::Site},
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
site::Site,
|
||||
},
|
||||
traits::DeleteableOrRemoveable,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
|
||||
|
@ -31,12 +36,13 @@ impl PerformCrud for GetCommunity {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
if data.name.is_none() && data.id.is_none() {
|
||||
return Err(LemmyError::from_message("no_id_given"));
|
||||
}
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.as_ref().map(|u| u.person.id);
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ use lemmy_api_common::{
|
|||
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::Community,
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
|
||||
},
|
||||
traits::Crud,
|
||||
|
@ -36,7 +36,13 @@ impl PerformCrud for RemoveCommunity {
|
|||
let community_id = data.community_id;
|
||||
let removed = data.removed;
|
||||
let updated_community = blocking(context.pool(), move |conn| {
|
||||
Community::update_removed(conn, community_id, removed)
|
||||
Community::update(
|
||||
conn,
|
||||
community_id,
|
||||
&CommunityUpdateForm::builder()
|
||||
.removed(Some(removed))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;
|
||||
|
|
|
@ -2,17 +2,18 @@ use crate::PerformCrud;
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
community::{CommunityResponse, EditCommunity},
|
||||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
utils::{blocking, get_local_user_view_from_jwt, local_site_to_slur_regex},
|
||||
};
|
||||
use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::{LanguageId, PersonId},
|
||||
source::{
|
||||
actor_language::{CommunityLanguage, SiteLanguage},
|
||||
community::{Community, CommunityForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
local_site::LocalSite,
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url},
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityModeratorView;
|
||||
use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
|
||||
|
@ -31,13 +32,15 @@ impl PerformCrud for EditCommunity {
|
|||
let data: &EditCommunity = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
let description = diesel_option_overwrite(&data.description);
|
||||
|
||||
check_slurs_opt(&data.title, &context.settings().slur_regex())?;
|
||||
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs_opt(&data.title, &slur_regex)?;
|
||||
check_slurs_opt(&data.description, &slur_regex)?;
|
||||
|
||||
// Verify its a mod (only mods can edit it)
|
||||
let community_id = data.community_id;
|
||||
|
@ -66,22 +69,14 @@ impl PerformCrud for EditCommunity {
|
|||
.await??;
|
||||
}
|
||||
|
||||
let read_community = blocking(context.pool(), move |conn| {
|
||||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let community_form = CommunityForm {
|
||||
name: read_community.name,
|
||||
title: data.title.to_owned().unwrap_or(read_community.title),
|
||||
description,
|
||||
icon,
|
||||
banner,
|
||||
nsfw: data.nsfw,
|
||||
posting_restricted_to_mods: data.posting_restricted_to_mods,
|
||||
updated: Some(naive_now()),
|
||||
..CommunityForm::default()
|
||||
};
|
||||
let community_form = CommunityUpdateForm::builder()
|
||||
.title(data.title.to_owned())
|
||||
.description(description)
|
||||
.icon(icon)
|
||||
.banner(banner)
|
||||
.nsfw(data.nsfw)
|
||||
.posting_restricted_to_mods(data.posting_restricted_to_mods)
|
||||
.build();
|
||||
|
||||
let community_id = data.community_id;
|
||||
let updated_community = blocking(context.pool(), move |conn| {
|
||||
|
|
|
@ -9,6 +9,7 @@ use lemmy_api_common::{
|
|||
check_community_deleted_or_removed,
|
||||
get_local_user_view_from_jwt,
|
||||
honeypot_check,
|
||||
local_site_to_slur_regex,
|
||||
mark_post_as_read,
|
||||
},
|
||||
};
|
||||
|
@ -23,10 +24,10 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::Community,
|
||||
post::{Post, PostForm, PostLike, PostLikeForm},
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
|
||||
},
|
||||
traits::{Crud, Likeable},
|
||||
utils::diesel_option_overwrite,
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityView;
|
||||
use lemmy_utils::{
|
||||
|
@ -52,15 +53,15 @@ impl PerformCrud for CreatePost {
|
|||
let data: &CreatePost = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let slur_regex = &context.settings().slur_regex();
|
||||
check_slurs(&data.name, slur_regex)?;
|
||||
check_slurs_opt(&data.body, slur_regex)?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs(&data.name, &slur_regex)?;
|
||||
check_slurs_opt(&data.body, &slur_regex)?;
|
||||
honeypot_check(&data.honeypot)?;
|
||||
|
||||
let data_url = data.url.as_ref();
|
||||
let url = Some(data_url.map(clean_url_params).map(Into::into)); // TODO no good way to handle a "clear"
|
||||
let body = diesel_option_overwrite(&data.body);
|
||||
let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
|
||||
|
||||
if !is_valid_post_title(&data.name) {
|
||||
return Err(LemmyError::from_message("invalid_post_title"));
|
||||
|
@ -89,7 +90,7 @@ impl PerformCrud for CreatePost {
|
|||
let (metadata_res, thumbnail_url) =
|
||||
fetch_site_data(context.client(), context.settings(), data_url).await;
|
||||
let (embed_title, embed_description, embed_video_url) = metadata_res
|
||||
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
|
||||
.map(|u| (u.title, u.description, u.embed_video_url))
|
||||
.unwrap_or_default();
|
||||
|
||||
let language_id = match data.language_id {
|
||||
|
@ -106,20 +107,19 @@ impl PerformCrud for CreatePost {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let post_form = PostForm {
|
||||
name: data.name.trim().to_owned(),
|
||||
url,
|
||||
body,
|
||||
community_id: data.community_id,
|
||||
creator_id: local_user_view.person.id,
|
||||
nsfw: data.nsfw,
|
||||
embed_title,
|
||||
embed_description,
|
||||
embed_video_url,
|
||||
language_id,
|
||||
thumbnail_url: Some(thumbnail_url),
|
||||
..PostForm::default()
|
||||
};
|
||||
let post_form = PostInsertForm::builder()
|
||||
.name(data.name.trim().to_owned())
|
||||
.url(url)
|
||||
.body(data.body.to_owned())
|
||||
.community_id(data.community_id)
|
||||
.creator_id(local_user_view.person.id)
|
||||
.nsfw(data.nsfw)
|
||||
.embed_title(embed_title)
|
||||
.embed_description(embed_description)
|
||||
.embed_video_url(embed_video_url)
|
||||
.language_id(language_id)
|
||||
.thumbnail_url(thumbnail_url)
|
||||
.build();
|
||||
|
||||
let inserted_post =
|
||||
match blocking(context.pool(), move |conn| Post::create(conn, &post_form)).await? {
|
||||
|
@ -143,7 +143,11 @@ impl PerformCrud for CreatePost {
|
|||
&inserted_post_id.to_string(),
|
||||
&protocol_and_hostname,
|
||||
)?;
|
||||
Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?)
|
||||
Ok(Post::update(
|
||||
conn,
|
||||
inserted_post_id,
|
||||
&PostUpdateForm::builder().ap_id(Some(apub_id)).build(),
|
||||
)?)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| e.with_message("couldnt_create_post"))?;
|
||||
|
|
|
@ -11,7 +11,10 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, post::Post},
|
||||
source::{
|
||||
community::Community,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
@ -56,7 +59,11 @@ impl PerformCrud for DeletePost {
|
|||
let post_id = data.post_id;
|
||||
let deleted = data.deleted;
|
||||
let updated_post = blocking(context.pool(), move |conn| {
|
||||
Post::update_deleted(conn, post_id, deleted)
|
||||
Post::update(
|
||||
conn,
|
||||
post_id,
|
||||
&PostUpdateForm::builder().deleted(Some(deleted)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -10,7 +10,10 @@ use lemmy_api_common::{
|
|||
},
|
||||
};
|
||||
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
|
||||
use lemmy_db_schema::{source::community::Community, traits::DeleteableOrRemoveable};
|
||||
use lemmy_db_schema::{
|
||||
source::{community::Community, local_site::LocalSite},
|
||||
traits::DeleteableOrRemoveable,
|
||||
};
|
||||
use lemmy_db_views::post_view::PostQuery;
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -29,13 +32,14 @@ impl PerformCrud for GetPosts {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let is_logged_in = local_user_view.is_some();
|
||||
|
||||
let sort = data.sort;
|
||||
let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?;
|
||||
let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
|
||||
|
||||
let page = data.page;
|
||||
let limit = data.limit;
|
||||
|
|
|
@ -6,7 +6,7 @@ use lemmy_api_common::{
|
|||
};
|
||||
use lemmy_db_schema::{
|
||||
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
|
||||
source::comment::Comment,
|
||||
source::{comment::Comment, local_site::LocalSite},
|
||||
traits::{Crud, DeleteableOrRemoveable},
|
||||
};
|
||||
use lemmy_db_views::structs::PostView;
|
||||
|
@ -28,8 +28,9 @@ impl PerformCrud for GetPost {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_id = local_user_view.map(|u| u.person.id);
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
community::Community,
|
||||
moderator::{ModRemovePost, ModRemovePostForm},
|
||||
post::Post,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -52,7 +52,11 @@ impl PerformCrud for RemovePost {
|
|||
let post_id = data.post_id;
|
||||
let removed = data.removed;
|
||||
let updated_post = blocking(context.pool(), move |conn| {
|
||||
Post::update_removed(conn, post_id, removed)
|
||||
Post::update(
|
||||
conn,
|
||||
post_id,
|
||||
&PostUpdateForm::builder().removed(Some(removed)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ use lemmy_api_common::{
|
|||
check_community_ban,
|
||||
check_community_deleted_or_removed,
|
||||
get_local_user_view_from_jwt,
|
||||
local_site_to_slur_regex,
|
||||
},
|
||||
};
|
||||
use lemmy_apub::protocol::activities::{
|
||||
|
@ -17,10 +18,11 @@ use lemmy_apub::protocol::activities::{
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
post::{Post, PostForm},
|
||||
local_site::LocalSite,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_option_overwrite, naive_now},
|
||||
utils::diesel_option_overwrite,
|
||||
};
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
|
@ -42,6 +44,7 @@ impl PerformCrud for EditPost {
|
|||
let data: &EditPost = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let data_url = data.url.as_ref();
|
||||
|
||||
|
@ -50,9 +53,9 @@ impl PerformCrud for EditPost {
|
|||
let url = Some(data_url.map(clean_url_params).map(Into::into));
|
||||
let body = diesel_option_overwrite(&data.body);
|
||||
|
||||
let slur_regex = &context.settings().slur_regex();
|
||||
check_slurs_opt(&data.name, slur_regex)?;
|
||||
check_slurs_opt(&data.body, slur_regex)?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs_opt(&data.name, &slur_regex)?;
|
||||
check_slurs_opt(&data.body, &slur_regex)?;
|
||||
|
||||
if let Some(name) = &data.name {
|
||||
if !is_valid_post_title(name) {
|
||||
|
@ -90,21 +93,17 @@ impl PerformCrud for EditPost {
|
|||
})
|
||||
.await??;
|
||||
|
||||
let post_form = PostForm {
|
||||
creator_id: orig_post.creator_id.to_owned(),
|
||||
community_id: orig_post.community_id,
|
||||
name: data.name.to_owned().unwrap_or(orig_post.name),
|
||||
url,
|
||||
body,
|
||||
nsfw: data.nsfw,
|
||||
updated: Some(naive_now()),
|
||||
embed_title,
|
||||
embed_description,
|
||||
embed_video_url,
|
||||
language_id: data.language_id,
|
||||
thumbnail_url: Some(thumbnail_url),
|
||||
..PostForm::default()
|
||||
};
|
||||
let post_form = PostUpdateForm::builder()
|
||||
.name(data.name.to_owned())
|
||||
.url(url)
|
||||
.body(body)
|
||||
.nsfw(data.nsfw)
|
||||
.embed_title(embed_title)
|
||||
.embed_description(embed_description)
|
||||
.embed_video_url(embed_video_url)
|
||||
.language_id(data.language_id)
|
||||
.thumbnail_url(Some(thumbnail_url))
|
||||
.build();
|
||||
|
||||
let post_id = data.post_id;
|
||||
let res = blocking(context.pool(), move |conn| {
|
||||
|
|
|
@ -7,6 +7,7 @@ use lemmy_api_common::{
|
|||
check_person_block,
|
||||
get_interface_language,
|
||||
get_local_user_view_from_jwt,
|
||||
local_site_to_slur_regex,
|
||||
send_email_to_user,
|
||||
},
|
||||
};
|
||||
|
@ -19,7 +20,10 @@ use lemmy_apub::{
|
|||
EndpointType,
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::private_message::{PrivateMessage, PrivateMessageForm},
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
|
@ -39,18 +43,20 @@ impl PerformCrud for CreatePrivateMessage {
|
|||
let data: &CreatePrivateMessage = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
let content_slurs_removed =
|
||||
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex());
|
||||
let content_slurs_removed = remove_slurs(
|
||||
&data.content.to_owned(),
|
||||
&local_site_to_slur_regex(&local_site),
|
||||
);
|
||||
|
||||
check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
|
||||
|
||||
let private_message_form = PrivateMessageForm {
|
||||
content: content_slurs_removed.to_owned(),
|
||||
creator_id: local_user_view.person.id,
|
||||
recipient_id: data.recipient_id,
|
||||
..PrivateMessageForm::default()
|
||||
};
|
||||
let private_message_form = PrivateMessageInsertForm::builder()
|
||||
.content(content_slurs_removed.to_owned())
|
||||
.creator_id(local_user_view.person.id)
|
||||
.recipient_id(data.recipient_id)
|
||||
.build();
|
||||
|
||||
let inserted_private_message = match blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::create(conn, &private_message_form)
|
||||
|
@ -76,10 +82,12 @@ impl PerformCrud for CreatePrivateMessage {
|
|||
&inserted_private_message_id.to_string(),
|
||||
&protocol_and_hostname,
|
||||
)?;
|
||||
Ok(PrivateMessage::update_ap_id(
|
||||
Ok(PrivateMessage::update(
|
||||
conn,
|
||||
inserted_private_message_id,
|
||||
apub_id,
|
||||
inserted_private_message.id,
|
||||
&PrivateMessageUpdateForm::builder()
|
||||
.ap_id(Some(apub_id))
|
||||
.build(),
|
||||
)?)
|
||||
},
|
||||
)
|
||||
|
|
|
@ -5,7 +5,10 @@ use lemmy_api_common::{
|
|||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
};
|
||||
use lemmy_apub::activities::deletion::send_apub_delete_private_message;
|
||||
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
|
||||
use lemmy_db_schema::{
|
||||
source::private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||
|
||||
|
@ -37,7 +40,13 @@ impl PerformCrud for DeletePrivateMessage {
|
|||
let private_message_id = data.private_message_id;
|
||||
let deleted = data.deleted;
|
||||
let updated_private_message = blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::update_deleted(conn, private_message_id, deleted)
|
||||
PrivateMessage::update(
|
||||
conn,
|
||||
private_message_id,
|
||||
&PrivateMessageUpdateForm::builder()
|
||||
.deleted(Some(deleted))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
|
||||
|
|
|
@ -2,13 +2,20 @@ use crate::PerformCrud;
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
private_message::{EditPrivateMessage, PrivateMessageResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt},
|
||||
utils::{blocking, get_local_user_view_from_jwt, local_site_to_slur_regex},
|
||||
};
|
||||
use lemmy_apub::protocol::activities::{
|
||||
create_or_update::private_message::CreateOrUpdatePrivateMessage,
|
||||
CreateOrUpdateType,
|
||||
};
|
||||
use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
local_site::LocalSite,
|
||||
private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
|
||||
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
|
||||
|
||||
|
@ -25,6 +32,7 @@ impl PerformCrud for EditPrivateMessage {
|
|||
let data: &EditPrivateMessage = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Checking permissions
|
||||
let private_message_id = data.private_message_id;
|
||||
|
@ -37,10 +45,17 @@ impl PerformCrud for EditPrivateMessage {
|
|||
}
|
||||
|
||||
// Doing the update
|
||||
let content_slurs_removed = remove_slurs(&data.content, &context.settings().slur_regex());
|
||||
let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site));
|
||||
let private_message_id = data.private_message_id;
|
||||
let updated_private_message = blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed)
|
||||
PrivateMessage::update(
|
||||
conn,
|
||||
private_message_id,
|
||||
&PrivateMessageUpdateForm::builder()
|
||||
.content(Some(content_slurs_removed))
|
||||
.updated(Some(Some(naive_now())))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;
|
||||
|
|
|
@ -3,19 +3,29 @@ use activitypub_federation::core::signatures::generate_actor_keypair;
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
site::{CreateSite, SiteResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin, site_description_length_check},
|
||||
utils::{
|
||||
blocking,
|
||||
get_local_user_view_from_jwt,
|
||||
is_admin,
|
||||
local_site_to_slur_regex,
|
||||
site_description_length_check,
|
||||
},
|
||||
};
|
||||
use lemmy_apub::generate_site_inbox_url;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::DbUrl,
|
||||
source::site::{Site, SiteForm},
|
||||
source::{
|
||||
local_site::{LocalSite, LocalSiteUpdateForm},
|
||||
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
|
||||
site::{Site, SiteUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
utils::{check_slurs, check_slurs_opt},
|
||||
utils::{check_application_question, check_slurs, check_slurs_opt},
|
||||
ConnectionId,
|
||||
};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -33,8 +43,9 @@ impl PerformCrud for CreateSite {
|
|||
) -> Result<SiteResponse, LemmyError> {
|
||||
let data: &CreateSite = self;
|
||||
|
||||
let read_site = Site::read_local;
|
||||
if blocking(context.pool(), read_site).await?.is_ok() {
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
if local_site.site_setup {
|
||||
return Err(LemmyError::from_message("site_already_exists"));
|
||||
};
|
||||
|
||||
|
@ -46,8 +57,9 @@ impl PerformCrud for CreateSite {
|
|||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
|
||||
check_slurs(&data.name, &context.settings().slur_regex())?;
|
||||
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs(&data.name, &slur_regex)?;
|
||||
check_slurs_opt(&data.description, &slur_regex)?;
|
||||
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
@ -56,35 +68,82 @@ impl PerformCrud for CreateSite {
|
|||
site_description_length_check(desc)?;
|
||||
}
|
||||
|
||||
let application_question = diesel_option_overwrite(&data.application_question);
|
||||
check_application_question(&application_question, &data.require_application)?;
|
||||
|
||||
let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
|
||||
let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
|
||||
let keypair = generate_actor_keypair()?;
|
||||
let site_form = SiteForm {
|
||||
name: data.name.to_owned(),
|
||||
sidebar,
|
||||
description,
|
||||
icon,
|
||||
banner,
|
||||
enable_downvotes: data.enable_downvotes,
|
||||
open_registration: data.open_registration,
|
||||
enable_nsfw: data.enable_nsfw,
|
||||
community_creation_admin_only: data.community_creation_admin_only,
|
||||
actor_id: Some(actor_id),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
inbox_url,
|
||||
private_key: Some(Some(keypair.private_key)),
|
||||
public_key: Some(keypair.public_key),
|
||||
default_theme: data.default_theme.clone(),
|
||||
default_post_listing_type: data.default_post_listing_type.clone(),
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
..SiteForm::default()
|
||||
};
|
||||
let site_form = SiteUpdateForm::builder()
|
||||
.name(Some(data.name.to_owned()))
|
||||
.sidebar(sidebar)
|
||||
.description(description)
|
||||
.icon(icon)
|
||||
.banner(banner)
|
||||
.actor_id(Some(actor_id))
|
||||
.last_refreshed_at(Some(naive_now()))
|
||||
.inbox_url(inbox_url)
|
||||
.private_key(Some(Some(keypair.private_key)))
|
||||
.public_key(Some(keypair.public_key))
|
||||
.build();
|
||||
|
||||
let create_site = move |conn: &mut _| Site::create(conn, &site_form);
|
||||
blocking(context.pool(), create_site)
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "site_already_exists"))?;
|
||||
let site_id = local_site.site_id;
|
||||
blocking(context.pool(), move |conn| {
|
||||
Site::update(conn, site_id, &site_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let local_site_form = LocalSiteUpdateForm::builder()
|
||||
// Set the site setup to true
|
||||
.site_setup(Some(true))
|
||||
.enable_downvotes(data.enable_downvotes)
|
||||
.open_registration(data.open_registration)
|
||||
.enable_nsfw(data.enable_nsfw)
|
||||
.community_creation_admin_only(data.community_creation_admin_only)
|
||||
.require_email_verification(data.require_email_verification)
|
||||
.require_application(data.require_application)
|
||||
.application_question(application_question)
|
||||
.private_instance(data.private_instance)
|
||||
.default_theme(data.default_theme.clone())
|
||||
.default_post_listing_type(data.default_post_listing_type.clone())
|
||||
.legal_information(diesel_option_overwrite(&data.legal_information))
|
||||
.application_email_admins(data.application_email_admins)
|
||||
.hide_modlog_mod_names(data.hide_modlog_mod_names)
|
||||
.updated(Some(Some(naive_now())))
|
||||
.slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
|
||||
.actor_name_max_length(data.actor_name_max_length)
|
||||
.federation_enabled(data.federation_enabled)
|
||||
.federation_debug(data.federation_debug)
|
||||
.federation_strict_allowlist(data.federation_strict_allowlist)
|
||||
.federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit)
|
||||
.federation_worker_count(data.federation_worker_count)
|
||||
.captcha_enabled(data.captcha_enabled)
|
||||
.captcha_difficulty(data.captcha_difficulty.to_owned())
|
||||
.build();
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalSite::update(conn, &local_site_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
|
||||
.message(data.rate_limit_message)
|
||||
.message_per_second(data.rate_limit_message_per_second)
|
||||
.post(data.rate_limit_post)
|
||||
.post_per_second(data.rate_limit_post_per_second)
|
||||
.register(data.rate_limit_register)
|
||||
.register_per_second(data.rate_limit_register_per_second)
|
||||
.image(data.rate_limit_image)
|
||||
.image_per_second(data.rate_limit_image_per_second)
|
||||
.comment(data.rate_limit_comment)
|
||||
.comment_per_second(data.rate_limit_comment_per_second)
|
||||
.search(data.rate_limit_search)
|
||||
.search_per_second(data.rate_limit_search_per_second)
|
||||
.build();
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalSiteRateLimit::update(conn, &local_site_rate_limit_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
use crate::PerformCrud;
|
||||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
person::Register,
|
||||
site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo},
|
||||
site::{GetSite, GetSiteResponse, MyUserInfo},
|
||||
utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_db_schema::source::{actor_language::SiteLanguage, language::Language};
|
||||
|
@ -16,56 +15,20 @@ use lemmy_db_views_actor::structs::{
|
|||
};
|
||||
use lemmy_utils::{error::LemmyError, version, ConnectionId};
|
||||
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
|
||||
use tracing::info;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for GetSite {
|
||||
type Response = GetSiteResponse;
|
||||
|
||||
#[tracing::instrument(skip(context, websocket_id))]
|
||||
#[tracing::instrument(skip(context, _websocket_id))]
|
||||
async fn perform(
|
||||
&self,
|
||||
context: &Data<LemmyContext>,
|
||||
websocket_id: Option<ConnectionId>,
|
||||
_websocket_id: Option<ConnectionId>,
|
||||
) -> Result<GetSiteResponse, LemmyError> {
|
||||
let data: &GetSite = self;
|
||||
|
||||
let site_view = match blocking(context.pool(), SiteView::read_local).await? {
|
||||
Ok(site_view) => Some(site_view),
|
||||
// If the site isn't created yet, check the setup
|
||||
Err(_) => {
|
||||
if let Some(setup) = context.settings().setup.as_ref() {
|
||||
let register = Register {
|
||||
username: setup.admin_username.to_owned(),
|
||||
email: setup.admin_email.clone().map(|s| s.into()),
|
||||
password: setup.admin_password.clone().into(),
|
||||
password_verify: setup.admin_password.clone().into(),
|
||||
show_nsfw: true,
|
||||
captcha_uuid: None,
|
||||
captcha_answer: None,
|
||||
honeypot: None,
|
||||
answer: None,
|
||||
};
|
||||
let admin_jwt = register
|
||||
.perform(context, websocket_id)
|
||||
.await?
|
||||
.jwt
|
||||
.expect("jwt is returned from registration on newly created site");
|
||||
info!("Admin {} created", setup.admin_username);
|
||||
|
||||
let create_site = CreateSite {
|
||||
name: setup.site_name.to_owned(),
|
||||
auth: admin_jwt,
|
||||
..CreateSite::default()
|
||||
};
|
||||
create_site.perform(context, websocket_id).await?;
|
||||
info!("Site {} created", setup.site_name);
|
||||
Some(blocking(context.pool(), SiteView::read_local).await??)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
};
|
||||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
|
||||
let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
|
||||
|
||||
|
@ -130,7 +93,8 @@ impl PerformCrud for GetSite {
|
|||
None
|
||||
};
|
||||
|
||||
let federated_instances = build_federated_instances(context.pool(), context.settings()).await?;
|
||||
let federated_instances =
|
||||
build_federated_instances(&site_view.local_site, context.pool()).await?;
|
||||
|
||||
let all_languages = blocking(context.pool(), Language::read_all).await??;
|
||||
let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??;
|
||||
|
|
|
@ -2,22 +2,36 @@ use crate::PerformCrud;
|
|||
use actix_web::web::Data;
|
||||
use lemmy_api_common::{
|
||||
site::{EditSite, SiteResponse},
|
||||
utils::{blocking, get_local_user_view_from_jwt, is_admin, site_description_length_check},
|
||||
utils::{
|
||||
blocking,
|
||||
get_local_user_view_from_jwt,
|
||||
is_admin,
|
||||
local_site_to_slur_regex,
|
||||
site_description_length_check,
|
||||
},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
federation_allowlist::FederationAllowList,
|
||||
federation_blocklist::FederationBlockList,
|
||||
local_site::{LocalSite, LocalSiteUpdateForm},
|
||||
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
|
||||
local_user::LocalUser,
|
||||
site::{Site, SiteForm},
|
||||
site::{Site, SiteUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
|
||||
ListingType,
|
||||
};
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
utils::{check_application_question, check_slurs_opt},
|
||||
ConnectionId,
|
||||
};
|
||||
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud};
|
||||
use std::{default::Default, str::FromStr};
|
||||
use std::str::FromStr;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl PerformCrud for EditSite {
|
||||
|
@ -32,32 +46,22 @@ impl PerformCrud for EditSite {
|
|||
let data: &EditSite = self;
|
||||
let local_user_view =
|
||||
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
// Make sure user is an admin
|
||||
is_admin(&local_user_view)?;
|
||||
|
||||
let local_site = blocking(context.pool(), Site::read_local).await??;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
|
||||
let sidebar = diesel_option_overwrite(&data.sidebar);
|
||||
let description = diesel_option_overwrite(&data.description);
|
||||
let application_question = diesel_option_overwrite(&data.application_question);
|
||||
let legal_information = diesel_option_overwrite(&data.legal_information);
|
||||
let icon = diesel_option_overwrite_to_url(&data.icon)?;
|
||||
let banner = diesel_option_overwrite_to_url(&data.banner)?;
|
||||
check_slurs_opt(&data.name, &slur_regex)?;
|
||||
check_slurs_opt(&data.description, &slur_regex)?;
|
||||
|
||||
check_slurs_opt(&data.name, &context.settings().slur_regex())?;
|
||||
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
|
||||
|
||||
if let Some(Some(desc)) = &description {
|
||||
if let Some(desc) = &data.description {
|
||||
site_description_length_check(desc)?;
|
||||
}
|
||||
|
||||
// Make sure if applications are required, that there is an application questionnaire
|
||||
if data.require_application.unwrap_or(false)
|
||||
&& application_question.as_ref().unwrap_or(&None).is_none()
|
||||
{
|
||||
return Err(LemmyError::from_message("application_question_required"));
|
||||
}
|
||||
let application_question = diesel_option_overwrite(&data.application_question);
|
||||
check_application_question(&application_question, &data.require_application)?;
|
||||
|
||||
if let Some(default_post_listing_type) = &data.default_post_listing_type {
|
||||
// only allow all or local as default listing types
|
||||
|
@ -69,7 +73,7 @@ impl PerformCrud for EditSite {
|
|||
}
|
||||
}
|
||||
|
||||
let site_id = local_site.id;
|
||||
let site_id = local_site.site_id;
|
||||
if let Some(discussion_languages) = data.discussion_languages.clone() {
|
||||
blocking(context.pool(), move |conn| {
|
||||
SiteLanguage::update(conn, discussion_languages.clone(), site_id)
|
||||
|
@ -77,41 +81,104 @@ impl PerformCrud for EditSite {
|
|||
.await??;
|
||||
}
|
||||
|
||||
let site_form = SiteForm {
|
||||
name: data.name.to_owned().unwrap_or(local_site.name),
|
||||
sidebar,
|
||||
description,
|
||||
icon,
|
||||
banner,
|
||||
updated: Some(naive_now()),
|
||||
enable_downvotes: data.enable_downvotes,
|
||||
open_registration: data.open_registration,
|
||||
enable_nsfw: data.enable_nsfw,
|
||||
community_creation_admin_only: data.community_creation_admin_only,
|
||||
require_email_verification: data.require_email_verification,
|
||||
require_application: data.require_application,
|
||||
application_question,
|
||||
private_instance: data.private_instance,
|
||||
default_theme: data.default_theme.clone(),
|
||||
default_post_listing_type: data.default_post_listing_type.clone(),
|
||||
legal_information,
|
||||
application_email_admins: data.application_email_admins,
|
||||
hide_modlog_mod_names: data.hide_modlog_mod_names,
|
||||
..SiteForm::default()
|
||||
};
|
||||
let name = data.name.to_owned();
|
||||
let site_form = SiteUpdateForm::builder()
|
||||
.name(name)
|
||||
.sidebar(diesel_option_overwrite(&data.sidebar))
|
||||
.description(diesel_option_overwrite(&data.description))
|
||||
.icon(diesel_option_overwrite_to_url(&data.icon)?)
|
||||
.banner(diesel_option_overwrite_to_url(&data.banner)?)
|
||||
.updated(Some(Some(naive_now())))
|
||||
.build();
|
||||
|
||||
let update_site = blocking(context.pool(), move |conn| {
|
||||
Site::update(conn, local_site.id, &site_form)
|
||||
blocking(context.pool(), move |conn| {
|
||||
Site::update(conn, site_id, &site_form)
|
||||
})
|
||||
.await?
|
||||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_site"))?;
|
||||
.await
|
||||
// Ignore errors for all these, so as to not throw errors if no update occurs
|
||||
// Diesel will throw an error for empty update forms
|
||||
.ok();
|
||||
|
||||
let local_site_form = LocalSiteUpdateForm::builder()
|
||||
.enable_downvotes(data.enable_downvotes)
|
||||
.open_registration(data.open_registration)
|
||||
.enable_nsfw(data.enable_nsfw)
|
||||
.community_creation_admin_only(data.community_creation_admin_only)
|
||||
.require_email_verification(data.require_email_verification)
|
||||
.require_application(data.require_application)
|
||||
.application_question(application_question)
|
||||
.private_instance(data.private_instance)
|
||||
.default_theme(data.default_theme.clone())
|
||||
.default_post_listing_type(data.default_post_listing_type.clone())
|
||||
.legal_information(diesel_option_overwrite(&data.legal_information))
|
||||
.application_email_admins(data.application_email_admins)
|
||||
.hide_modlog_mod_names(data.hide_modlog_mod_names)
|
||||
.updated(Some(Some(naive_now())))
|
||||
.slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex))
|
||||
.actor_name_max_length(data.actor_name_max_length)
|
||||
.federation_enabled(data.federation_enabled)
|
||||
.federation_debug(data.federation_debug)
|
||||
.federation_strict_allowlist(data.federation_strict_allowlist)
|
||||
.federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit)
|
||||
.federation_worker_count(data.federation_worker_count)
|
||||
.captcha_enabled(data.captcha_enabled)
|
||||
.captcha_difficulty(data.captcha_difficulty.to_owned())
|
||||
.build();
|
||||
|
||||
let update_local_site = blocking(context.pool(), move |conn| {
|
||||
LocalSite::update(conn, &local_site_form)
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder()
|
||||
.message(data.rate_limit_message)
|
||||
.message_per_second(data.rate_limit_message_per_second)
|
||||
.post(data.rate_limit_post)
|
||||
.post_per_second(data.rate_limit_post_per_second)
|
||||
.register(data.rate_limit_register)
|
||||
.register_per_second(data.rate_limit_register_per_second)
|
||||
.image(data.rate_limit_image)
|
||||
.image_per_second(data.rate_limit_image_per_second)
|
||||
.comment(data.rate_limit_comment)
|
||||
.comment_per_second(data.rate_limit_comment_per_second)
|
||||
.search(data.rate_limit_search)
|
||||
.search_per_second(data.rate_limit_search_per_second)
|
||||
.build();
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalSiteRateLimit::update(conn, &local_site_rate_limit_form)
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Replace the blocked and allowed instances
|
||||
let allowed = data.allowed_instances.to_owned();
|
||||
blocking(context.pool(), move |conn| {
|
||||
FederationAllowList::replace(conn, allowed)
|
||||
})
|
||||
.await??;
|
||||
let blocked = data.blocked_instances.to_owned();
|
||||
blocking(context.pool(), move |conn| {
|
||||
FederationBlockList::replace(conn, blocked)
|
||||
})
|
||||
.await??;
|
||||
|
||||
// TODO can't think of a better way to do this.
|
||||
// If the server suddenly requires email verification, or required applications, no old users
|
||||
// will be able to log in. It really only wants this to be a requirement for NEW signups.
|
||||
// So if it was set from false, to true, you need to update all current users columns to be verified.
|
||||
|
||||
if !local_site.require_application && update_site.require_application {
|
||||
let new_require_application = update_local_site
|
||||
.as_ref()
|
||||
.map(|ols| {
|
||||
ols
|
||||
.as_ref()
|
||||
.map(|ls| ls.require_application)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if !local_site.require_application && new_require_application {
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::set_all_users_registration_applications_accepted(conn)
|
||||
})
|
||||
|
@ -119,7 +186,16 @@ impl PerformCrud for EditSite {
|
|||
.map_err(|e| LemmyError::from_error_message(e, "couldnt_set_all_registrations_accepted"))?;
|
||||
}
|
||||
|
||||
if !local_site.require_email_verification && update_site.require_email_verification {
|
||||
let new_require_email_verification = update_local_site
|
||||
.as_ref()
|
||||
.map(|ols| {
|
||||
ols
|
||||
.as_ref()
|
||||
.map(|ls| ls.require_email_verification)
|
||||
.unwrap_or(false)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if !local_site.require_email_verification && new_require_email_verification {
|
||||
blocking(context.pool(), move |conn| {
|
||||
LocalUser::set_all_users_email_verified(conn)
|
||||
})
|
||||
|
|
|
@ -6,6 +6,7 @@ use lemmy_api_common::{
|
|||
utils::{
|
||||
blocking,
|
||||
honeypot_check,
|
||||
local_site_to_slur_regex,
|
||||
password_length_check,
|
||||
send_new_applicant_email_to_admins,
|
||||
send_verification_email,
|
||||
|
@ -20,14 +21,13 @@ use lemmy_apub::{
|
|||
use lemmy_db_schema::{
|
||||
aggregates::structs::PersonAggregates,
|
||||
source::{
|
||||
local_user::{LocalUser, LocalUserForm},
|
||||
person::{Person, PersonForm},
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationForm},
|
||||
site::Site,
|
||||
local_user::{LocalUser, LocalUserInsertForm},
|
||||
person::{Person, PersonInsertForm},
|
||||
registration_application::{RegistrationApplication, RegistrationApplicationInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_views::structs::LocalUserView;
|
||||
use lemmy_db_views::structs::{LocalUserView, SiteView};
|
||||
use lemmy_db_views_actor::structs::PersonViewSafe;
|
||||
use lemmy_utils::{
|
||||
claims::Claims,
|
||||
|
@ -49,27 +49,21 @@ impl PerformCrud for Register {
|
|||
) -> Result<LoginResponse, LemmyError> {
|
||||
let data: &Register = self;
|
||||
|
||||
// no email verification, or applications if the site is not setup yet
|
||||
let (mut email_verification, mut require_application) = (false, false);
|
||||
let site_view = blocking(context.pool(), SiteView::read_local).await??;
|
||||
let local_site = site_view.local_site;
|
||||
|
||||
// Make sure site has open registration
|
||||
let site = blocking(context.pool(), Site::read_local).await?;
|
||||
if let Ok(site) = &site {
|
||||
if !site.open_registration {
|
||||
if !local_site.open_registration {
|
||||
return Err(LemmyError::from_message("registration_closed"));
|
||||
}
|
||||
email_verification = site.require_email_verification;
|
||||
require_application = site.require_application;
|
||||
}
|
||||
|
||||
password_length_check(&data.password)?;
|
||||
honeypot_check(&data.honeypot)?;
|
||||
|
||||
if email_verification && data.email.is_none() {
|
||||
if local_site.require_email_verification && data.email.is_none() {
|
||||
return Err(LemmyError::from_message("email_required"));
|
||||
}
|
||||
|
||||
if require_application && data.answer.is_none() {
|
||||
if local_site.require_application && data.answer.is_none() {
|
||||
return Err(LemmyError::from_message(
|
||||
"registration_application_answer_required",
|
||||
));
|
||||
|
@ -87,7 +81,7 @@ impl PerformCrud for Register {
|
|||
.await??;
|
||||
|
||||
// If its not the admin, check the captcha
|
||||
if !no_admins && context.settings().captcha.enabled {
|
||||
if !no_admins && local_site.captcha_enabled {
|
||||
let check = context
|
||||
.chat_server()
|
||||
.send(CheckCaptcha {
|
||||
|
@ -106,12 +100,12 @@ impl PerformCrud for Register {
|
|||
}
|
||||
}
|
||||
|
||||
let slur_regex = &context.settings().slur_regex();
|
||||
check_slurs(&data.username, slur_regex)?;
|
||||
check_slurs_opt(&data.answer, slur_regex)?;
|
||||
let slur_regex = local_site_to_slur_regex(&local_site);
|
||||
check_slurs(&data.username, &slur_regex)?;
|
||||
check_slurs_opt(&data.answer, &slur_regex)?;
|
||||
|
||||
let actor_keypair = generate_actor_keypair()?;
|
||||
if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) {
|
||||
if !is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize) {
|
||||
return Err(LemmyError::from_message("invalid_username"));
|
||||
}
|
||||
let actor_id = generate_local_apub_endpoint(
|
||||
|
@ -123,16 +117,16 @@ impl PerformCrud for Register {
|
|||
// We have to create both a person, and local_user
|
||||
|
||||
// Register the new person
|
||||
let person_form = PersonForm {
|
||||
name: data.username.to_owned(),
|
||||
actor_id: Some(actor_id.clone()),
|
||||
private_key: Some(Some(actor_keypair.private_key)),
|
||||
public_key: Some(actor_keypair.public_key),
|
||||
inbox_url: Some(generate_inbox_url(&actor_id)?),
|
||||
shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
|
||||
admin: Some(no_admins),
|
||||
..PersonForm::default()
|
||||
};
|
||||
let person_form = PersonInsertForm::builder()
|
||||
.name(data.username.to_owned())
|
||||
.actor_id(Some(actor_id.clone()))
|
||||
.private_key(Some(actor_keypair.private_key))
|
||||
.public_key(actor_keypair.public_key)
|
||||
.inbox_url(Some(generate_inbox_url(&actor_id)?))
|
||||
.shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?))
|
||||
.admin(Some(no_admins))
|
||||
.instance_id(site_view.site.instance_id)
|
||||
.build();
|
||||
|
||||
// insert the person
|
||||
let inserted_person = blocking(context.pool(), move |conn| {
|
||||
|
@ -142,17 +136,15 @@ impl PerformCrud for Register {
|
|||
.map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
|
||||
|
||||
// Create the local user
|
||||
let local_user_form = LocalUserForm {
|
||||
person_id: Some(inserted_person.id),
|
||||
email: Some(data.email.as_deref().map(|s| s.to_lowercase())),
|
||||
password_encrypted: Some(data.password.to_string()),
|
||||
show_nsfw: Some(data.show_nsfw),
|
||||
email_verified: Some(false),
|
||||
..LocalUserForm::default()
|
||||
};
|
||||
let local_user_form = LocalUserInsertForm::builder()
|
||||
.person_id(inserted_person.id)
|
||||
.email(data.email.as_deref().map(|s| s.to_lowercase()))
|
||||
.password_encrypted(data.password.to_string())
|
||||
.show_nsfw(Some(data.show_nsfw))
|
||||
.build();
|
||||
|
||||
let inserted_local_user = match blocking(context.pool(), move |conn| {
|
||||
LocalUser::register(conn, &local_user_form)
|
||||
LocalUser::create(conn, &local_user_form)
|
||||
})
|
||||
.await?
|
||||
{
|
||||
|
@ -176,13 +168,12 @@ impl PerformCrud for Register {
|
|||
}
|
||||
};
|
||||
|
||||
if require_application {
|
||||
if local_site.require_application {
|
||||
// Create the registration application
|
||||
let form = RegistrationApplicationForm {
|
||||
local_user_id: Some(inserted_local_user.id),
|
||||
let form = RegistrationApplicationInsertForm {
|
||||
local_user_id: inserted_local_user.id,
|
||||
// We already made sure answer was not null above
|
||||
answer: data.answer.to_owned(),
|
||||
..RegistrationApplicationForm::default()
|
||||
answer: data.answer.to_owned().expect("must have an answer"),
|
||||
};
|
||||
|
||||
blocking(context.pool(), move |conn| {
|
||||
|
@ -192,7 +183,7 @@ impl PerformCrud for Register {
|
|||
}
|
||||
|
||||
// Email the admins
|
||||
if site.map(|s| s.application_email_admins).unwrap_or(false) {
|
||||
if local_site.application_email_admins {
|
||||
send_new_applicant_email_to_admins(&data.username, context.pool(), context.settings())
|
||||
.await?;
|
||||
}
|
||||
|
@ -204,7 +195,7 @@ impl PerformCrud for Register {
|
|||
};
|
||||
|
||||
// Log the user in directly if email verification and application aren't required
|
||||
if !require_application && !email_verification {
|
||||
if !local_site.require_application && !local_site.require_email_verification {
|
||||
login_response.jwt = Some(
|
||||
Claims::jwt(
|
||||
inserted_local_user.id.0,
|
||||
|
@ -214,7 +205,7 @@ impl PerformCrud for Register {
|
|||
.into(),
|
||||
);
|
||||
} else {
|
||||
if email_verification {
|
||||
if local_site.require_email_verification {
|
||||
let local_user_view = LocalUserView {
|
||||
local_user: inserted_local_user,
|
||||
person: inserted_person,
|
||||
|
@ -226,12 +217,13 @@ impl PerformCrud for Register {
|
|||
.email
|
||||
.clone()
|
||||
.expect("email was provided");
|
||||
|
||||
send_verification_email(&local_user_view, &email, context.pool(), context.settings())
|
||||
.await?;
|
||||
login_response.verify_email_sent = true;
|
||||
}
|
||||
|
||||
if require_application {
|
||||
if local_site.require_application {
|
||||
login_response.registration_created = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ use lemmy_api_common::{
|
|||
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
|
||||
};
|
||||
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson};
|
||||
use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type};
|
||||
use lemmy_db_schema::{
|
||||
source::{local_site::LocalSite, person::Person},
|
||||
utils::post_to_comment_sort_type,
|
||||
};
|
||||
use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery};
|
||||
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
|
||||
use lemmy_utils::{error::LemmyError, ConnectionId};
|
||||
|
@ -31,7 +34,9 @@ impl PerformCrud for GetPersonDetails {
|
|||
let local_user_view =
|
||||
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
|
||||
.await?;
|
||||
check_private_instance(&local_user_view, context.pool()).await?;
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await??;
|
||||
|
||||
check_private_instance(&local_user_view, &local_site)?;
|
||||
|
||||
let person_details_id = match data.person_id {
|
||||
Some(id) => id,
|
||||
|
|
|
@ -9,6 +9,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::block::block_user::BlockUser,
|
||||
|
@ -33,7 +35,7 @@ use lemmy_db_schema::{
|
|||
CommunityPersonBanForm,
|
||||
},
|
||||
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::Person,
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::{Bannable, Crud, Followable},
|
||||
};
|
||||
|
@ -123,6 +125,10 @@ impl ActivityHandler for BlockUser {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
match self
|
||||
.target
|
||||
|
@ -177,7 +183,14 @@ impl ActivityHandler for BlockUser {
|
|||
match target {
|
||||
SiteOrCommunity::Site(_site) => {
|
||||
let blocked_person = blocking(context.pool(), move |conn| {
|
||||
Person::ban_person(conn, blocked_person.id, true, expires)
|
||||
Person::update(
|
||||
conn,
|
||||
blocked_person.id,
|
||||
&PersonUpdateForm::builder()
|
||||
.banned(Some(true))
|
||||
.ban_expires(Some(expires))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
if self.remove_data.unwrap_or(false) {
|
||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
|||
verify_is_public,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||
|
@ -24,7 +26,7 @@ use lemmy_db_schema::{
|
|||
source::{
|
||||
community::{CommunityPersonBan, CommunityPersonBanForm},
|
||||
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
|
||||
person::Person,
|
||||
person::{Person, PersonUpdateForm},
|
||||
},
|
||||
traits::{Bannable, Crud},
|
||||
};
|
||||
|
@ -90,6 +92,10 @@ impl ActivityHandler for UndoBlockUser {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
|
@ -121,7 +127,14 @@ impl ActivityHandler for UndoBlockUser {
|
|||
{
|
||||
SiteOrCommunity::Site(_site) => {
|
||||
let blocked_person = blocking(context.pool(), move |conn| {
|
||||
Person::ban_person(conn, blocked_person.id, false, expires)
|
||||
Person::update(
|
||||
conn,
|
||||
blocked_person.id,
|
||||
&PersonUpdateForm::builder()
|
||||
.banned(Some(false))
|
||||
.ban_expires(Some(expires))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -12,6 +12,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
generate_moderators_url,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
|
@ -84,6 +86,10 @@ impl ActivityHandler for AddMod {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
insert_activity,
|
||||
objects::community::ApubCommunity,
|
||||
protocol::{
|
||||
|
@ -11,6 +13,7 @@ use crate::{
|
|||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
|
||||
use activitystreams_kinds::{activity::AnnounceType, public};
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use tracing::debug;
|
||||
|
@ -84,9 +87,13 @@ impl ActivityHandler for AnnounceActivity {
|
|||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
_context: &Data<LemmyContext>,
|
||||
context: &Data<LemmyContext>,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
generate_moderators_url,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
|
@ -84,6 +86,10 @@ impl ActivityHandler for RemoveMod {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::community::report::Report,
|
||||
|
@ -74,6 +76,10 @@ impl ActivityHandler for Report {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
let community = self.to[0]
|
||||
.dereference(context, local_instance(context), request_counter)
|
||||
.await?;
|
||||
|
|
|
@ -7,6 +7,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::community::update::UpdateCommunity,
|
||||
|
@ -19,10 +21,7 @@ use activitypub_federation::{
|
|||
};
|
||||
use activitystreams_kinds::{activity::UpdateType, public};
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
source::community::{Community, CommunityForm},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_db_schema::{source::community::Community, traits::Crud};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
|
||||
use url::Url;
|
||||
|
@ -72,6 +71,9 @@ impl ActivityHandler for UpdateCommunity {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
|
@ -101,19 +103,10 @@ impl ActivityHandler for UpdateCommunity {
|
|||
) -> Result<(), LemmyError> {
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
|
||||
let updated_community = self.object.into_form();
|
||||
let cf = CommunityForm {
|
||||
name: updated_community.name,
|
||||
title: updated_community.title,
|
||||
description: updated_community.description,
|
||||
nsfw: updated_community.nsfw,
|
||||
// TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours
|
||||
icon: updated_community.icon,
|
||||
banner: updated_community.banner,
|
||||
..CommunityForm::default()
|
||||
};
|
||||
let community_update_form = self.object.into_update_form();
|
||||
|
||||
let updated_community = blocking(context.pool(), move |conn| {
|
||||
Community::update(conn, community.id, &cf)
|
||||
Community::update(conn, community.id, &community_update_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
mentions::MentionOrValue,
|
||||
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
|
||||
|
@ -115,6 +117,10 @@ impl ActivityHandler for CreateOrUpdateComment {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let post = self.object.get_parents(context, request_counter).await?.0;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
|
|
|
@ -8,6 +8,8 @@ use crate::{
|
|||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
|
||||
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
|
||||
ActorType,
|
||||
|
@ -93,6 +95,9 @@ impl ActivityHandler for CreateOrUpdatePost {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_is_public(&self.to, &self.cc)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
objects::{person::ApubPerson, private_message::ApubPrivateMessage},
|
||||
protocol::activities::{
|
||||
create_or_update::private_message::CreateOrUpdatePrivateMessage,
|
||||
|
@ -69,6 +71,10 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
verify_domains_match(self.actor.inner(), self.object.id.inner())?;
|
||||
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
|
||||
|
|
|
@ -4,6 +4,8 @@ use crate::{
|
|||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||
generate_activity_id,
|
||||
},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
|
||||
|
@ -14,8 +16,8 @@ use anyhow::anyhow;
|
|||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
|
@ -24,7 +26,7 @@ use lemmy_db_schema::{
|
|||
ModRemovePost,
|
||||
ModRemovePostForm,
|
||||
},
|
||||
post::Post,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -55,6 +57,9 @@ impl ActivityHandler for Delete {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -150,7 +155,11 @@ pub(in crate::activities) async fn receive_remove_action(
|
|||
})
|
||||
.await??;
|
||||
let deleted_community = blocking(context.pool(), move |conn| {
|
||||
Community::update_removed(conn, community.id, true)
|
||||
Community::update(
|
||||
conn,
|
||||
community.id,
|
||||
&CommunityUpdateForm::builder().removed(Some(true)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -168,7 +177,11 @@ pub(in crate::activities) async fn receive_remove_action(
|
|||
})
|
||||
.await??;
|
||||
let removed_post = blocking(context.pool(), move |conn| {
|
||||
Post::update_removed(conn, post.id, true)
|
||||
Post::update(
|
||||
conn,
|
||||
post.id,
|
||||
&PostUpdateForm::builder().removed(Some(true)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -186,7 +199,11 @@ pub(in crate::activities) async fn receive_remove_action(
|
|||
})
|
||||
.await??;
|
||||
let removed_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_removed(conn, comment.id, true)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment.id,
|
||||
&CommentUpdateForm::builder().removed(Some(true)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{instance::remote_instance_inboxes, person::ApubPerson},
|
||||
protocol::activities::deletion::delete_user::DeleteUser,
|
||||
|
@ -11,7 +13,7 @@ use activitypub_federation::{
|
|||
utils::verify_urls_match,
|
||||
};
|
||||
use activitystreams_kinds::{activity::DeleteType, public};
|
||||
use lemmy_api_common::utils::delete_user_account;
|
||||
use lemmy_api_common::utils::{blocking, delete_user_account};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
@ -36,6 +38,9 @@ impl ActivityHandler for DeleteUser {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_is_public(&self.to, &[])?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
verify_urls_match(self.actor.inner(), self.object.inner())?;
|
||||
|
|
|
@ -28,11 +28,11 @@ use activitystreams_kinds::public;
|
|||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
person::Person,
|
||||
post::Post,
|
||||
private_message::PrivateMessage,
|
||||
post::{Post, PostUpdateForm},
|
||||
private_message::{PrivateMessage, PrivateMessageUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -239,7 +239,13 @@ async fn receive_delete_action(
|
|||
}
|
||||
|
||||
let community = blocking(context.pool(), move |conn| {
|
||||
Community::update_deleted(conn, community.id, deleted)
|
||||
Community::update(
|
||||
conn,
|
||||
community.id,
|
||||
&CommunityUpdateForm::builder()
|
||||
.deleted(Some(deleted))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_community_ws_message(
|
||||
|
@ -254,7 +260,11 @@ async fn receive_delete_action(
|
|||
DeletableObjects::Post(post) => {
|
||||
if deleted != post.deleted {
|
||||
let deleted_post = blocking(context.pool(), move |conn| {
|
||||
Post::update_deleted(conn, post.id, deleted)
|
||||
Post::update(
|
||||
conn,
|
||||
post.id,
|
||||
&PostUpdateForm::builder().deleted(Some(deleted)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_post_ws_message(
|
||||
|
@ -270,7 +280,11 @@ async fn receive_delete_action(
|
|||
DeletableObjects::Comment(comment) => {
|
||||
if deleted != comment.deleted {
|
||||
let deleted_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_deleted(conn, comment.id, deleted)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment.id,
|
||||
&CommentUpdateForm::builder().deleted(Some(deleted)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_comment_ws_message_simple(
|
||||
|
@ -283,7 +297,13 @@ async fn receive_delete_action(
|
|||
}
|
||||
DeletableObjects::PrivateMessage(pm) => {
|
||||
let deleted_private_message = blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::update_deleted(conn, pm.id, deleted)
|
||||
PrivateMessage::update(
|
||||
conn,
|
||||
pm.id,
|
||||
&PrivateMessageUpdateForm::builder()
|
||||
.deleted(Some(deleted))
|
||||
.build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ use crate::{
|
|||
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
|
||||
generate_activity_id,
|
||||
},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
|
||||
|
@ -13,8 +15,8 @@ use activitystreams_kinds::activity::UndoType;
|
|||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::Comment,
|
||||
community::Community,
|
||||
comment::{Comment, CommentUpdateForm},
|
||||
community::{Community, CommunityUpdateForm},
|
||||
moderator::{
|
||||
ModRemoveComment,
|
||||
ModRemoveCommentForm,
|
||||
|
@ -23,7 +25,7 @@ use lemmy_db_schema::{
|
|||
ModRemovePost,
|
||||
ModRemovePostForm,
|
||||
},
|
||||
post::Post,
|
||||
post::{Post, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -54,6 +56,9 @@ impl ActivityHandler for UndoDelete {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
verify_delete_activity(
|
||||
&self.object,
|
||||
|
@ -148,7 +153,11 @@ impl UndoDelete {
|
|||
})
|
||||
.await??;
|
||||
let deleted_community = blocking(context.pool(), move |conn| {
|
||||
Community::update_removed(conn, community.id, false)
|
||||
Community::update(
|
||||
conn,
|
||||
community.id,
|
||||
&CommunityUpdateForm::builder().removed(Some(false)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?;
|
||||
|
@ -165,7 +174,11 @@ impl UndoDelete {
|
|||
})
|
||||
.await??;
|
||||
let removed_post = blocking(context.pool(), move |conn| {
|
||||
Post::update_removed(conn, post.id, false)
|
||||
Post::update(
|
||||
conn,
|
||||
post.id,
|
||||
&PostUpdateForm::builder().removed(Some(false)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_post_ws_message(removed_post.id, EditPost, None, None, context).await?;
|
||||
|
@ -182,7 +195,11 @@ impl UndoDelete {
|
|||
})
|
||||
.await??;
|
||||
let removed_comment = blocking(context.pool(), move |conn| {
|
||||
Comment::update_removed(conn, comment.id, false)
|
||||
Comment::update(
|
||||
conn,
|
||||
comment.id,
|
||||
&CommentUpdateForm::builder().removed(Some(false)).build(),
|
||||
)
|
||||
})
|
||||
.await??;
|
||||
send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?;
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||
ActorType,
|
||||
|
@ -67,6 +69,10 @@ impl ActivityHandler for AcceptFollowCommunity {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
|
||||
verify_urls_match(self.actor.inner(), self.object.object.inner())?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
Ok(())
|
||||
|
|
|
@ -5,6 +5,8 @@ use crate::{
|
|||
verify_person,
|
||||
verify_person_in_community,
|
||||
},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
|
||||
|
@ -84,6 +86,9 @@ impl ActivityHandler for FollowCommunity {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
let community = self
|
||||
.object
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_person},
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
|
||||
|
@ -63,6 +65,9 @@ impl ActivityHandler for UndoFollowCommunity {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
verify_person(&self.actor, context, request_counter).await?;
|
||||
self.object.verify(context, request_counter).await?;
|
||||
|
|
|
@ -14,7 +14,10 @@ use activitypub_federation::{
|
|||
use activitystreams_kinds::public;
|
||||
use anyhow::anyhow;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{community::Community, local_site::LocalSite},
|
||||
};
|
||||
use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
|
@ -167,9 +170,14 @@ where
|
|||
ActorT: Actor + ActorType,
|
||||
Activity: ActivityHandler<Error = LemmyError>,
|
||||
{
|
||||
if !context.settings().federation.enabled {
|
||||
let federation_enabled = blocking(context.pool(), &LocalSite::read)
|
||||
.await?
|
||||
.map(|l| l.federation_enabled)
|
||||
.unwrap_or(false);
|
||||
if !federation_enabled {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("Sending activity {}", activity.id().to_string());
|
||||
let activity = WithContext::new(activity, CONTEXT.deref().clone());
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@ use crate::{
|
|||
voting::{undo_vote_comment, undo_vote_post},
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::voting::{
|
||||
|
@ -82,6 +84,9 @@ impl ActivityHandler for UndoVote {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
|
||||
|
|
|
@ -6,6 +6,8 @@ use crate::{
|
|||
voting::{vote_comment, vote_post},
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
check_apub_id_valid,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::activities::voting::vote::{Vote, VoteType},
|
||||
|
@ -17,7 +19,7 @@ use anyhow::anyhow;
|
|||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
newtypes::CommunityId,
|
||||
source::{community::Community, post::Post, site::Site},
|
||||
source::{community::Community, local_site::LocalSite, post::Post},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
|
@ -81,10 +83,16 @@ impl ActivityHandler for Vote {
|
|||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
check_apub_id_valid(self.id(), &local_site_data, context.settings())
|
||||
.map_err(LemmyError::from_message)?;
|
||||
let community = self.get_community(context, request_counter).await?;
|
||||
verify_person_in_community(&self.actor, &community, context, request_counter).await?;
|
||||
let site = blocking(context.pool(), Site::read_local).await??;
|
||||
if self.kind == VoteType::Dislike && !site.enable_downvotes {
|
||||
let enable_downvotes = blocking(context.pool(), LocalSite::read)
|
||||
.await?
|
||||
.map(|l| l.enable_downvotes)
|
||||
.unwrap_or(true);
|
||||
if self.kind == VoteType::Dislike && !enable_downvotes {
|
||||
return Err(anyhow!("Downvotes disabled").into());
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -152,7 +152,8 @@ mod tests {
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
community::Community,
|
||||
person::{Person, PersonForm},
|
||||
instance::Instance,
|
||||
person::{Person, PersonInsertForm},
|
||||
site::Site,
|
||||
},
|
||||
traits::Crud,
|
||||
|
@ -168,11 +169,14 @@ mod tests {
|
|||
let community = parse_lemmy_community(&context).await;
|
||||
let community_id = community.id;
|
||||
|
||||
let old_mod = PersonForm {
|
||||
name: "holly".into(),
|
||||
public_key: Some("pubkey".to_string()),
|
||||
..PersonForm::default()
|
||||
};
|
||||
let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap();
|
||||
|
||||
let old_mod = PersonInsertForm::builder()
|
||||
.name("holly".into())
|
||||
.public_key("pubkey".to_string())
|
||||
.instance_id(inserted_instance.id)
|
||||
.build();
|
||||
|
||||
let old_mod = Person::create(conn, &old_mod).unwrap();
|
||||
let community_moderator_form = CommunityModeratorForm {
|
||||
community_id: community.id,
|
||||
|
@ -210,5 +214,6 @@ mod tests {
|
|||
Person::delete(conn, new_mod.id).unwrap();
|
||||
Community::delete(conn, community_context.0.id).unwrap();
|
||||
Site::delete(conn, site.id).unwrap();
|
||||
Instance::delete(conn, inserted_instance.id).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@ use crate::{local_instance, ActorType};
|
|||
use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
|
||||
use anyhow::anyhow;
|
||||
use itertools::Itertools;
|
||||
use lemmy_db_schema::newtypes::DbUrl;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{newtypes::DbUrl, source::local_site::LocalSite};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
@ -47,8 +48,14 @@ where
|
|||
);
|
||||
debug!("Fetching webfinger url: {}", &fetch_url);
|
||||
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await?;
|
||||
let http_fetch_retry_limit = local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_http_fetch_retry_limit)
|
||||
.unwrap_or(25);
|
||||
|
||||
*request_counter += 1;
|
||||
if *request_counter > context.settings().federation.http_fetch_retry_limit {
|
||||
if *request_counter > http_fetch_retry_limit {
|
||||
return Err(LemmyError::from_message("Request retry limit reached"));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,13 +19,9 @@ use actix_web::{
|
|||
web,
|
||||
};
|
||||
use http_signature_normalization_actix::digest::middleware::VerifyDigest;
|
||||
use lemmy_utils::settings::structs::Settings;
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
|
||||
if settings.federation.enabled {
|
||||
println!("federation enabled, host is {}", settings.hostname);
|
||||
|
||||
pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
cfg
|
||||
.route("/", web::get().to(get_apub_site_http))
|
||||
.route("/site_outbox", web::get().to(get_apub_site_outbox))
|
||||
|
@ -64,7 +60,6 @@ pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
|
|||
.route("/site_inbox", web::post().to(get_apub_site_inbox)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Without this, things like webfinger or RSS feeds stop working, as all requests seem to get
|
||||
/// routed into the inbox service (because it covers the root path). So we filter out anything that
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
|
||||
use actix_web::{web, HttpRequest, HttpResponse};
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::source::site::Site;
|
||||
use lemmy_db_views::structs::SiteView;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use url::Url;
|
||||
|
@ -15,7 +15,10 @@ use url::Url;
|
|||
pub(crate) async fn get_apub_site_http(
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
let site: ApubSite = blocking(context.pool(), Site::read_local).await??.into();
|
||||
let site: ApubSite = blocking(context.pool(), SiteView::read_local)
|
||||
.await??
|
||||
.site
|
||||
.into();
|
||||
|
||||
let apub = site.into_apub(&context).await?;
|
||||
Ok(create_apub_response(&apub))
|
||||
|
|
|
@ -6,13 +6,14 @@ use activitypub_federation::{
|
|||
LocalInstance,
|
||||
};
|
||||
use anyhow::Context;
|
||||
use diesel::PgConnection;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool};
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
location_info,
|
||||
settings::{structs::Settings, SETTINGS},
|
||||
use lemmy_db_schema::{
|
||||
newtypes::DbUrl,
|
||||
source::{activity::Activity, instance::Instance, local_site::LocalSite},
|
||||
utils::DbPool,
|
||||
};
|
||||
use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use url::{ParseError, Url};
|
||||
|
@ -31,16 +32,35 @@ static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
|
|||
});
|
||||
|
||||
// TODO: store this in context? but its only used in this crate, no need to expose it elsewhere
|
||||
// TODO this singleton needs to be redone to account for live data.
|
||||
fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
|
||||
static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::new();
|
||||
LOCAL_INSTANCE.get_or_init(|| {
|
||||
let conn = &mut context
|
||||
.pool()
|
||||
.get()
|
||||
.expect("getting connection for LOCAL_INSTANCE init");
|
||||
// Local site may be missing
|
||||
let local_site = &LocalSite::read(conn);
|
||||
let worker_count = local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_worker_count)
|
||||
.unwrap_or(64) as u64;
|
||||
let http_fetch_retry_limit = local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_http_fetch_retry_limit)
|
||||
.unwrap_or(25);
|
||||
let federation_debug = local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_debug)
|
||||
.unwrap_or(true);
|
||||
|
||||
let settings = InstanceSettings::builder()
|
||||
.http_fetch_retry_limit(context.settings().federation.http_fetch_retry_limit)
|
||||
.worker_count(context.settings().federation.worker_count)
|
||||
.debug(context.settings().federation.debug)
|
||||
.http_fetch_retry_limit(http_fetch_retry_limit)
|
||||
.worker_count(worker_count)
|
||||
.debug(federation_debug)
|
||||
// TODO No idea why, but you can't pass context.settings() to the verify_url_function closure
|
||||
// without the value getting captured.
|
||||
.verify_url_function(|url| check_apub_id_valid(url, &SETTINGS))
|
||||
.http_signature_compat(true)
|
||||
.build()
|
||||
.expect("configure federation");
|
||||
|
@ -62,8 +82,13 @@ fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
|
|||
///
|
||||
/// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
|
||||
/// post/comment in a local community.
|
||||
#[tracing::instrument(skip(settings))]
|
||||
fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'static str> {
|
||||
#[tracing::instrument(skip(settings, local_site_data))]
|
||||
// TODO This function needs to be called by incoming activities
|
||||
fn check_apub_id_valid(
|
||||
apub_id: &Url,
|
||||
local_site_data: &LocalSiteData,
|
||||
settings: &Settings,
|
||||
) -> Result<(), &'static str> {
|
||||
let domain = apub_id.domain().expect("apud id has domain").to_string();
|
||||
let local_instance = settings
|
||||
.get_hostname_without_port()
|
||||
|
@ -72,7 +97,12 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if !settings.federation.enabled {
|
||||
if !local_site_data
|
||||
.local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_enabled)
|
||||
.unwrap_or(true)
|
||||
{
|
||||
return Err("Federation disabled");
|
||||
}
|
||||
|
||||
|
@ -80,13 +110,13 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
|
|||
return Err("Invalid protocol scheme");
|
||||
}
|
||||
|
||||
if let Some(blocked) = settings.to_owned().federation.blocked_instances {
|
||||
if let Some(blocked) = local_site_data.blocked_instances.as_ref() {
|
||||
if blocked.contains(&domain) {
|
||||
return Err("Domain is blocked");
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(allowed) = settings.to_owned().federation.allowed_instances {
|
||||
if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
|
||||
if !allowed.contains(&domain) {
|
||||
return Err("Domain is not in allowlist");
|
||||
}
|
||||
|
@ -95,13 +125,40 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(settings))]
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct LocalSiteData {
|
||||
local_site: Option<LocalSite>,
|
||||
allowed_instances: Option<Vec<String>>,
|
||||
blocked_instances: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
pub(crate) fn fetch_local_site_data(
|
||||
conn: &mut PgConnection,
|
||||
) -> Result<LocalSiteData, diesel::result::Error> {
|
||||
// LocalSite may be missing
|
||||
let local_site = LocalSite::read(conn).ok();
|
||||
let allowed = Instance::allowlist(conn)?;
|
||||
let blocked = Instance::blocklist(conn)?;
|
||||
|
||||
// These can return empty vectors, so convert them to options
|
||||
let allowed_instances = (!allowed.is_empty()).then(|| allowed);
|
||||
let blocked_instances = (!blocked.is_empty()).then(|| blocked);
|
||||
|
||||
Ok(LocalSiteData {
|
||||
local_site,
|
||||
allowed_instances,
|
||||
blocked_instances,
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip(settings, local_site_data))]
|
||||
pub(crate) fn check_apub_id_valid_with_strictness(
|
||||
apub_id: &Url,
|
||||
is_strict: bool,
|
||||
local_site_data: &LocalSiteData,
|
||||
settings: &Settings,
|
||||
) -> Result<(), LemmyError> {
|
||||
check_apub_id_valid(apub_id, settings).map_err(LemmyError::from_message)?;
|
||||
check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?;
|
||||
let domain = apub_id.domain().expect("apud id has domain").to_string();
|
||||
let local_instance = settings
|
||||
.get_hostname_without_port()
|
||||
|
@ -110,15 +167,20 @@ pub(crate) fn check_apub_id_valid_with_strictness(
|
|||
return Ok(());
|
||||
}
|
||||
|
||||
if let Some(mut allowed) = settings.to_owned().federation.allowed_instances {
|
||||
if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
|
||||
// Only check allowlist if this is a community, or strict allowlist is enabled.
|
||||
let strict_allowlist = settings.to_owned().federation.strict_allowlist;
|
||||
let strict_allowlist = local_site_data
|
||||
.local_site
|
||||
.as_ref()
|
||||
.map(|l| l.federation_strict_allowlist)
|
||||
.unwrap_or(true);
|
||||
if is_strict || strict_allowlist {
|
||||
// need to allow this explicitly because apub receive might contain objects from our local
|
||||
// instance.
|
||||
allowed.push(local_instance);
|
||||
let mut allowed_and_local = allowed.to_owned();
|
||||
allowed_and_local.push(local_instance);
|
||||
|
||||
if !allowed.contains(&domain) {
|
||||
if !allowed_and_local.contains(&domain) {
|
||||
return Err(LemmyError::from_message(
|
||||
"Federation forbidden by strict allowlist",
|
||||
));
|
||||
|
@ -203,7 +265,7 @@ async fn insert_activity(
|
|||
let ap_id = ap_id.to_owned().into();
|
||||
Ok(
|
||||
blocking(pool, move |conn| {
|
||||
Activity::insert(conn, ap_id, activity, local, sensitive)
|
||||
Activity::insert(conn, ap_id, activity, local, Some(sensitive))
|
||||
})
|
||||
.await??,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
activities::{verify_is_public, verify_person_in_community},
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
mentions::collect_non_local_mentions,
|
||||
objects::{read_from_string_or_source, verify_is_remote_object},
|
||||
|
@ -18,11 +19,12 @@ use activitypub_federation::{
|
|||
};
|
||||
use activitystreams_kinds::{object::NoteType, public};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
comment::{Comment, CommentForm},
|
||||
comment::{Comment, CommentInsertForm, CommentUpdateForm},
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
person::Person,
|
||||
post::Post,
|
||||
},
|
||||
|
@ -81,7 +83,8 @@ impl ApubObject for ApubComment {
|
|||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
if !self.deleted {
|
||||
blocking(context.pool(), move |conn| {
|
||||
Comment::update_deleted(conn, self.id, true)
|
||||
let form = CommentUpdateForm::builder().deleted(Some(true)).build();
|
||||
Comment::update(conn, self.id, &form)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
|
@ -148,7 +151,14 @@ impl ApubObject for ApubComment {
|
|||
Community::read(conn, community_id)
|
||||
})
|
||||
.await??;
|
||||
check_apub_id_valid_with_strictness(note.id.inner(), community.local, context.settings())?;
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
|
||||
check_apub_id_valid_with_strictness(
|
||||
note.id.inner(),
|
||||
community.local,
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
verify_is_remote_object(note.id.inner(), context.settings())?;
|
||||
verify_person_in_community(
|
||||
¬e.attributed_to,
|
||||
|
@ -179,10 +189,13 @@ impl ApubObject for ApubComment {
|
|||
let (post, parent_comment) = note.get_parents(context, request_counter).await?;
|
||||
|
||||
let content = read_from_string_or_source(¬e.content, ¬e.media_type, ¬e.source);
|
||||
let content_slurs_removed = remove_slurs(&content, &context.settings().slur_regex());
|
||||
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await?.ok();
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||
let content_slurs_removed = remove_slurs(&content, slur_regex);
|
||||
let language_id = LanguageTag::to_language_id_single(note.language, context.pool()).await?;
|
||||
|
||||
let form = CommentForm {
|
||||
let form = CommentInsertForm {
|
||||
creator_id: creator.id,
|
||||
post_id: post.id,
|
||||
content: content_slurs_removed,
|
||||
|
@ -244,6 +257,7 @@ pub(crate) mod tests {
|
|||
Community::delete(conn, data.1.id).unwrap();
|
||||
Person::delete(conn, data.0.id).unwrap();
|
||||
Site::delete(conn, data.3.id).unwrap();
|
||||
LocalSite::delete(conn).unwrap();
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
collections::{community_moderators::ApubCommunityModerators, CommunityContext},
|
||||
fetch_local_site_data,
|
||||
generate_moderators_url,
|
||||
generate_outbox_url,
|
||||
local_instance,
|
||||
|
@ -21,8 +22,12 @@ use chrono::NaiveDateTime;
|
|||
use itertools::Itertools;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_db_schema::{
|
||||
source::{actor_language::CommunityLanguage, community::Community},
|
||||
traits::ApubActor,
|
||||
source::{
|
||||
actor_language::CommunityLanguage,
|
||||
community::{Community, CommunityUpdateForm},
|
||||
instance::Instance,
|
||||
},
|
||||
traits::{ApubActor, Crud},
|
||||
};
|
||||
use lemmy_db_views_actor::structs::CommunityFollowerView;
|
||||
use lemmy_utils::{
|
||||
|
@ -78,7 +83,8 @@ impl ApubObject for ApubCommunity {
|
|||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
blocking(context.pool(), move |conn| {
|
||||
Community::update_deleted(conn, self.id, true)
|
||||
let form = CommunityUpdateForm::builder().deleted(Some(true)).build();
|
||||
Community::update(conn, self.id, &form)
|
||||
})
|
||||
.await??;
|
||||
Ok(())
|
||||
|
@ -138,11 +144,17 @@ impl ApubObject for ApubCommunity {
|
|||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubCommunity, LemmyError> {
|
||||
let form = Group::into_form(group.clone());
|
||||
let apub_id = group.id.inner().to_owned();
|
||||
let instance = blocking(context.pool(), move |conn| {
|
||||
Instance::create_from_actor_id(conn, &apub_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let form = Group::into_insert_form(group.clone(), instance.id);
|
||||
let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
|
||||
|
||||
let community: ApubCommunity = blocking(context.pool(), move |conn| {
|
||||
let community = Community::upsert(conn, &form)?;
|
||||
let community = Community::create(conn, &form)?;
|
||||
CommunityLanguage::update(conn, languages, community.id)?;
|
||||
Ok::<Community, diesel::result::Error>(community)
|
||||
})
|
||||
|
@ -205,6 +217,7 @@ impl ApubCommunity {
|
|||
) -> Result<Vec<Url>, LemmyError> {
|
||||
let id = self.id;
|
||||
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
let follows = blocking(context.pool(), move |conn| {
|
||||
CommunityFollowerView::for_community(conn, id)
|
||||
})
|
||||
|
@ -221,7 +234,10 @@ impl ApubCommunity {
|
|||
.unique()
|
||||
.filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname))
|
||||
// Don't send to blocked instances
|
||||
.filter(|inbox| check_apub_id_valid_with_strictness(inbox, false, context.settings()).is_ok())
|
||||
.filter(|inbox| {
|
||||
check_apub_id_valid_with_strictness(inbox, false, &local_site_data, context.settings())
|
||||
.is_ok()
|
||||
})
|
||||
.collect();
|
||||
|
||||
Ok(inboxes)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::read_from_string_or_source_opt,
|
||||
protocol::{
|
||||
|
@ -19,12 +20,14 @@ use activitypub_federation::{
|
|||
utils::verify_domains_match,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
source::{
|
||||
actor_language::SiteLanguage,
|
||||
site::{Site, SiteForm},
|
||||
instance::Instance as DbInstance,
|
||||
site::{Site, SiteInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
utils::{naive_now, DbPool},
|
||||
};
|
||||
use lemmy_utils::{
|
||||
|
@ -114,10 +117,13 @@ impl ApubObject for ApubSite {
|
|||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
check_apub_id_valid_with_strictness(apub.id.inner(), true, data.settings())?;
|
||||
let local_site_data = blocking(data.pool(), fetch_local_site_data).await??;
|
||||
|
||||
check_apub_id_valid_with_strictness(apub.id.inner(), true, &local_site_data, data.settings())?;
|
||||
verify_domains_match(expected_domain, apub.id.inner())?;
|
||||
|
||||
let slur_regex = &data.settings().slur_regex();
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
|
||||
|
||||
check_slurs(&apub.name, slur_regex)?;
|
||||
check_slurs_opt(&apub.summary, slur_regex)?;
|
||||
Ok(())
|
||||
|
@ -129,27 +135,30 @@ impl ApubObject for ApubSite {
|
|||
data: &Self::DataType,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<Self, LemmyError> {
|
||||
let site_form = SiteForm {
|
||||
let apub_id = apub.id.inner().to_owned();
|
||||
let instance = blocking(data.pool(), move |conn| {
|
||||
DbInstance::create_from_actor_id(conn, &apub_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let site_form = SiteInsertForm {
|
||||
name: apub.name.clone(),
|
||||
sidebar: Some(read_from_string_or_source_opt(
|
||||
&apub.content,
|
||||
&None,
|
||||
&apub.source,
|
||||
)),
|
||||
sidebar: read_from_string_or_source_opt(&apub.content, &None, &apub.source),
|
||||
updated: apub.updated.map(|u| u.clone().naive_local()),
|
||||
icon: Some(apub.icon.clone().map(|i| i.url.into())),
|
||||
banner: Some(apub.image.clone().map(|i| i.url.into())),
|
||||
description: Some(apub.summary.clone()),
|
||||
icon: apub.icon.clone().map(|i| i.url.into()),
|
||||
banner: apub.image.clone().map(|i| i.url.into()),
|
||||
description: apub.summary.clone(),
|
||||
actor_id: Some(apub.id.clone().into()),
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
inbox_url: Some(apub.inbox.clone().into()),
|
||||
public_key: Some(apub.public_key.public_key_pem.clone()),
|
||||
..SiteForm::default()
|
||||
private_key: None,
|
||||
instance_id: instance.id,
|
||||
};
|
||||
let languages = LanguageTag::to_language_id_multiple(apub.language, data.pool()).await?;
|
||||
|
||||
let site = blocking(data.pool(), move |conn| {
|
||||
let site = Site::upsert(conn, &site_form)?;
|
||||
let site = Site::create(conn, &site_form)?;
|
||||
SiteLanguage::update(conn, languages, site.id)?;
|
||||
Ok::<Site, diesel::result::Error>(site)
|
||||
})
|
||||
|
|
|
@ -67,7 +67,7 @@ pub(crate) mod tests {
|
|||
};
|
||||
use lemmy_utils::{
|
||||
error::LemmyError,
|
||||
rate_limit::{rate_limiter::RateLimiter, RateLimit},
|
||||
rate_limit::{rate_limiter::RateLimiter, RateLimit, RateLimitConfig},
|
||||
settings::SETTINGS,
|
||||
};
|
||||
use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
|
||||
|
@ -96,10 +96,6 @@ pub(crate) mod tests {
|
|||
// call this to run migrations
|
||||
establish_unpooled_connection();
|
||||
let settings = SETTINGS.to_owned();
|
||||
let rate_limiter = RateLimit {
|
||||
rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
|
||||
rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(),
|
||||
};
|
||||
let client = Client::builder()
|
||||
.user_agent(build_user_agent(&settings))
|
||||
.build()
|
||||
|
@ -122,6 +118,14 @@ pub(crate) mod tests {
|
|||
async fn x() -> Result<String, LemmyError> {
|
||||
Ok("".to_string())
|
||||
}
|
||||
|
||||
let rate_limit_config = RateLimitConfig::builder().build();
|
||||
|
||||
let rate_limiter = RateLimit {
|
||||
rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
|
||||
rate_limit_config,
|
||||
};
|
||||
|
||||
let chat_server = ChatServer::startup(
|
||||
pool.clone(),
|
||||
rate_limiter,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
generate_outbox_url,
|
||||
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
|
||||
protocol::{
|
||||
|
@ -18,10 +19,13 @@ use activitypub_federation::{
|
|||
utils::verify_domains_match,
|
||||
};
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::utils::blocking;
|
||||
use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex};
|
||||
use lemmy_db_schema::{
|
||||
source::person::{Person as DbPerson, PersonForm},
|
||||
traits::ApubActor,
|
||||
source::{
|
||||
instance::Instance,
|
||||
person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
|
||||
},
|
||||
traits::{ApubActor, Crud},
|
||||
utils::naive_now,
|
||||
};
|
||||
use lemmy_utils::{
|
||||
|
@ -76,7 +80,8 @@ impl ApubObject for ApubPerson {
|
|||
#[tracing::instrument(skip_all)]
|
||||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
blocking(context.pool(), move |conn| {
|
||||
DbPerson::update_deleted(conn, self.id, true)
|
||||
let form = PersonUpdateForm::builder().deleted(Some(true)).build();
|
||||
DbPerson::update(conn, self.id, &form)
|
||||
})
|
||||
.await??;
|
||||
Ok(())
|
||||
|
@ -119,12 +124,20 @@ impl ApubObject for ApubPerson {
|
|||
context: &LemmyContext,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(person.id.inner(), expected_domain)?;
|
||||
check_apub_id_valid_with_strictness(person.id.inner(), false, context.settings())?;
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
|
||||
|
||||
let slur_regex = &context.settings().slur_regex();
|
||||
check_slurs(&person.preferred_username, slur_regex)?;
|
||||
check_slurs_opt(&person.name, slur_regex)?;
|
||||
|
||||
verify_domains_match(person.id.inner(), expected_domain)?;
|
||||
check_apub_id_valid_with_strictness(
|
||||
person.id.inner(),
|
||||
false,
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
|
||||
let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
|
||||
check_slurs_opt(&bio, slur_regex)?;
|
||||
Ok(())
|
||||
|
@ -136,34 +149,37 @@ impl ApubObject for ApubPerson {
|
|||
context: &LemmyContext,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<ApubPerson, LemmyError> {
|
||||
let person_form = PersonForm {
|
||||
let apub_id = person.id.inner().to_owned();
|
||||
let instance = blocking(context.pool(), move |conn| {
|
||||
Instance::create_from_actor_id(conn, &apub_id)
|
||||
})
|
||||
.await??;
|
||||
|
||||
let person_form = PersonInsertForm {
|
||||
name: person.preferred_username,
|
||||
display_name: Some(person.name),
|
||||
display_name: person.name,
|
||||
banned: None,
|
||||
ban_expires: None,
|
||||
deleted: None,
|
||||
avatar: Some(person.icon.map(|i| i.url.into())),
|
||||
banner: Some(person.image.map(|i| i.url.into())),
|
||||
avatar: person.icon.map(|i| i.url.into()),
|
||||
banner: person.image.map(|i| i.url.into()),
|
||||
published: person.published.map(|u| u.naive_local()),
|
||||
updated: person.updated.map(|u| u.naive_local()),
|
||||
actor_id: Some(person.id.into()),
|
||||
bio: Some(read_from_string_or_source_opt(
|
||||
&person.summary,
|
||||
&None,
|
||||
&person.source,
|
||||
)),
|
||||
bio: read_from_string_or_source_opt(&person.summary, &None, &person.source),
|
||||
local: Some(false),
|
||||
admin: Some(false),
|
||||
bot_account: Some(person.kind == UserTypes::Service),
|
||||
private_key: None,
|
||||
public_key: Some(person.public_key.public_key_pem),
|
||||
public_key: person.public_key.public_key_pem,
|
||||
last_refreshed_at: Some(naive_now()),
|
||||
inbox_url: Some(person.inbox.into()),
|
||||
shared_inbox_url: Some(person.endpoints.map(|e| e.shared_inbox.into())),
|
||||
matrix_user_id: Some(person.matrix_user_id),
|
||||
shared_inbox_url: person.endpoints.map(|e| e.shared_inbox.into()),
|
||||
matrix_user_id: person.matrix_user_id,
|
||||
instance_id: instance.id,
|
||||
};
|
||||
let person = blocking(context.pool(), move |conn| {
|
||||
DbPerson::upsert(conn, &person_form)
|
||||
DbPerson::create(conn, &person_form)
|
||||
})
|
||||
.await??;
|
||||
|
||||
|
@ -230,22 +246,19 @@ pub(crate) mod tests {
|
|||
#[serial]
|
||||
async fn test_parse_lemmy_person() {
|
||||
let context = init_context();
|
||||
let conn = &mut context.pool().get().unwrap();
|
||||
let (person, site) = parse_lemmy_person(&context).await;
|
||||
|
||||
assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string()));
|
||||
assert!(!person.local);
|
||||
assert_eq!(person.bio.as_ref().unwrap().len(), 39);
|
||||
|
||||
DbPerson::delete(conn, person.id).unwrap();
|
||||
Site::delete(conn, site.id).unwrap();
|
||||
cleanup((person, site), &context);
|
||||
}
|
||||
|
||||
#[actix_rt::test]
|
||||
#[serial]
|
||||
async fn test_parse_pleroma_person() {
|
||||
let context = init_context();
|
||||
let conn = &mut context.pool().get().unwrap();
|
||||
|
||||
// create and parse a fake pleroma instance actor, to avoid network request during test
|
||||
let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
|
||||
|
@ -272,7 +285,12 @@ pub(crate) mod tests {
|
|||
assert_eq!(request_counter, 0);
|
||||
assert_eq!(person.bio.as_ref().unwrap().len(), 873);
|
||||
|
||||
DbPerson::delete(conn, person.id).unwrap();
|
||||
Site::delete(conn, site.id).unwrap();
|
||||
cleanup((person, site), &context);
|
||||
}
|
||||
|
||||
fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) {
|
||||
let conn = &mut context.pool().get().unwrap();
|
||||
DbPerson::delete(conn, data.0.id).unwrap();
|
||||
Site::delete(conn, data.1.id).unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use crate::{
|
||||
activities::{verify_is_public, verify_person_in_community},
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::{read_from_string_or_source_opt, verify_is_remote_object},
|
||||
protocol::{
|
||||
|
@ -20,14 +21,18 @@ use activitypub_federation::{
|
|||
};
|
||||
use activitystreams_kinds::public;
|
||||
use chrono::NaiveDateTime;
|
||||
use lemmy_api_common::{request::fetch_site_data, utils::blocking};
|
||||
use lemmy_api_common::{
|
||||
request::fetch_site_data,
|
||||
utils::{blocking, local_site_opt_to_slur_regex},
|
||||
};
|
||||
use lemmy_db_schema::{
|
||||
self,
|
||||
source::{
|
||||
community::Community,
|
||||
local_site::LocalSite,
|
||||
moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
|
||||
person::Person,
|
||||
post::{Post, PostForm},
|
||||
post::{Post, PostInsertForm, PostUpdateForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -84,7 +89,8 @@ impl ApubObject for ApubPost {
|
|||
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
|
||||
if !self.deleted {
|
||||
blocking(context.pool(), move |conn| {
|
||||
Post::update_deleted(conn, self.id, true)
|
||||
let form = PostUpdateForm::builder().deleted(Some(true)).build();
|
||||
Post::update(conn, self.id, &form)
|
||||
})
|
||||
.await??;
|
||||
}
|
||||
|
@ -140,10 +146,20 @@ impl ApubObject for ApubPost {
|
|||
verify_is_remote_object(page.id.inner(), context.settings())?;
|
||||
};
|
||||
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
|
||||
let community = page.extract_community(context, request_counter).await?;
|
||||
check_apub_id_valid_with_strictness(page.id.inner(), community.local, context.settings())?;
|
||||
check_apub_id_valid_with_strictness(
|
||||
page.id.inner(),
|
||||
community.local,
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
verify_person_in_community(&page.creator()?, &community, context, request_counter).await?;
|
||||
check_slurs(&page.name, &context.settings().slur_regex())?;
|
||||
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
|
||||
check_slurs(&page.name, slur_regex)?;
|
||||
|
||||
verify_domains_match(page.creator()?.inner(), page.id.inner())?;
|
||||
verify_is_public(&page.to, &page.cc)?;
|
||||
Ok(())
|
||||
|
@ -181,16 +197,19 @@ impl ApubObject for ApubPost {
|
|||
(None, page.image.map(|i| i.url.into()))
|
||||
};
|
||||
let (embed_title, embed_description, embed_video_url) = metadata_res
|
||||
.map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url)))
|
||||
.map(|u| (u.title, u.description, u.embed_video_url))
|
||||
.unwrap_or_default();
|
||||
let local_site = blocking(context.pool(), LocalSite::read).await?.ok();
|
||||
let slur_regex = &local_site_opt_to_slur_regex(&local_site);
|
||||
|
||||
let body_slurs_removed =
|
||||
read_from_string_or_source_opt(&page.content, &page.media_type, &page.source)
|
||||
.map(|s| Some(remove_slurs(&s, &context.settings().slur_regex())));
|
||||
.map(|s| remove_slurs(&s, slur_regex));
|
||||
let language_id = LanguageTag::to_language_id_single(page.language, context.pool()).await?;
|
||||
|
||||
PostForm {
|
||||
PostInsertForm {
|
||||
name: page.name.clone(),
|
||||
url: Some(url.map(Into::into)),
|
||||
url: url.map(Into::into),
|
||||
body: body_slurs_removed,
|
||||
creator_id: creator.id,
|
||||
community_id: community.id,
|
||||
|
@ -204,23 +223,22 @@ impl ApubObject for ApubPost {
|
|||
embed_title,
|
||||
embed_description,
|
||||
embed_video_url,
|
||||
thumbnail_url: Some(thumbnail_url),
|
||||
thumbnail_url,
|
||||
ap_id: Some(page.id.clone().into()),
|
||||
local: Some(false),
|
||||
language_id,
|
||||
}
|
||||
} else {
|
||||
// if is mod action, only update locked/stickied fields, nothing else
|
||||
PostForm {
|
||||
name: page.name.clone(),
|
||||
creator_id: creator.id,
|
||||
community_id: community.id,
|
||||
locked: page.comments_enabled.map(|e| !e),
|
||||
stickied: page.stickied,
|
||||
updated: page.updated.map(|u| u.naive_local()),
|
||||
ap_id: Some(page.id.clone().into()),
|
||||
..Default::default()
|
||||
}
|
||||
PostInsertForm::builder()
|
||||
.name(page.name.clone())
|
||||
.creator_id(creator.id)
|
||||
.community_id(community.id)
|
||||
.ap_id(Some(page.id.clone().into()))
|
||||
.locked(page.comments_enabled.map(|e| !e))
|
||||
.stickied(page.stickied)
|
||||
.updated(page.updated.map(|u| u.naive_local()))
|
||||
.build()
|
||||
};
|
||||
|
||||
// read existing, local post if any (for generating mod log)
|
||||
|
@ -228,7 +246,7 @@ impl ApubObject for ApubPost {
|
|||
.dereference_local(context)
|
||||
.await;
|
||||
|
||||
let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??;
|
||||
let post = blocking(context.pool(), move |conn| Post::create(conn, &form)).await??;
|
||||
|
||||
// write mod log entries for sticky/lock
|
||||
if Page::is_stickied_changed(&old_post, &page.stickied) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
check_apub_id_valid_with_strictness,
|
||||
fetch_local_site_data,
|
||||
local_instance,
|
||||
objects::read_from_string_or_source,
|
||||
protocol::{
|
||||
|
@ -18,7 +19,7 @@ use lemmy_api_common::utils::{blocking, check_person_block};
|
|||
use lemmy_db_schema::{
|
||||
source::{
|
||||
person::Person,
|
||||
private_message::{PrivateMessage, PrivateMessageForm},
|
||||
private_message::{PrivateMessage, PrivateMessageInsertForm},
|
||||
},
|
||||
traits::Crud,
|
||||
};
|
||||
|
@ -108,7 +109,15 @@ impl ApubObject for ApubPrivateMessage {
|
|||
) -> Result<(), LemmyError> {
|
||||
verify_domains_match(note.id.inner(), expected_domain)?;
|
||||
verify_domains_match(note.attributed_to.inner(), note.id.inner())?;
|
||||
check_apub_id_valid_with_strictness(note.id.inner(), false, context.settings())?;
|
||||
|
||||
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
|
||||
|
||||
check_apub_id_valid_with_strictness(
|
||||
note.id.inner(),
|
||||
false,
|
||||
&local_site_data,
|
||||
context.settings(),
|
||||
)?;
|
||||
let person = note
|
||||
.attributed_to
|
||||
.dereference(context, local_instance(context), request_counter)
|
||||
|
@ -134,7 +143,7 @@ impl ApubObject for ApubPrivateMessage {
|
|||
.await?;
|
||||
check_person_block(creator.id, recipient.id, context.pool()).await?;
|
||||
|
||||
let form = PrivateMessageForm {
|
||||
let form = PrivateMessageInsertForm {
|
||||
creator_id: creator.id,
|
||||
recipient_id: recipient.id,
|
||||
content: read_from_string_or_source(¬e.content, &None, ¬e.source),
|
||||
|
@ -146,7 +155,7 @@ impl ApubObject for ApubPrivateMessage {
|
|||
local: Some(false),
|
||||
};
|
||||
let pm = blocking(context.pool(), move |conn| {
|
||||
PrivateMessage::upsert(conn, &form)
|
||||
PrivateMessage::create(conn, &form)
|
||||
})
|
||||
.await??;
|
||||
Ok(pm.into())
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue