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:
Dessalines 2022-10-27 05:24:07 -04:00 committed by GitHub
parent 276a8c2bd3
commit 235cc8b228
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
179 changed files with 4602 additions and 4013 deletions

4
Cargo.lock generated
View file

@ -2005,6 +2005,7 @@ dependencies = [
"lemmy_db_views_moderator", "lemmy_db_views_moderator",
"lemmy_utils", "lemmy_utils",
"percent-encoding", "percent-encoding",
"regex",
"reqwest", "reqwest",
"reqwest-middleware", "reqwest-middleware",
"rosetta-i18n", "rosetta-i18n",
@ -2096,6 +2097,7 @@ dependencies = [
"sha2", "sha2",
"strum", "strum",
"strum_macros", "strum_macros",
"typed-builder",
"url", "url",
] ]
@ -2110,6 +2112,7 @@ dependencies = [
"serial_test", "serial_test",
"tracing", "tracing",
"typed-builder", "typed-builder",
"url",
] ]
[[package]] [[package]]
@ -2227,6 +2230,7 @@ dependencies = [
"strum_macros", "strum_macros",
"tracing", "tracing",
"tracing-error", "tracing-error",
"typed-builder",
"url", "url",
"uuid 1.1.2", "uuid 1.1.2",
] ]

View file

@ -4,11 +4,11 @@
"browser": true "browser": true
}, },
"plugins": [ "plugins": [
"jane" "@typescript-eslint"
], ],
"extends": [ "extends": [
"plugin:jane/recommended", "eslint:recommended",
"plugin:jane/typescript" "plugin:@typescript-eslint/recommended"
], ],
"parser": "@typescript-eslint/parser", "parser": "@typescript-eslint/parser",
"parserOptions": { "parserOptions": {

View file

@ -1,4 +1,4 @@
module.exports = Object.assign(require('eslint-plugin-jane/prettier-ts'), { module.exports = Object.assign(require("eslint-plugin-prettier"), {
arrowParens: 'avoid', arrowParens: "avoid",
semi: true, semi: true,
}); });

View file

@ -14,15 +14,17 @@
"devDependencies": { "devDependencies": {
"@sniptt/monads": "^0.5.10", "@sniptt/monads": "^0.5.10",
"@types/jest": "^26.0.23", "@types/jest": "^26.0.23",
"@typescript-eslint/eslint-plugin": "^5.21.0",
"@typescript-eslint/parser": "^5.21.0",
"class-transformer": "^0.5.1", "class-transformer": "^0.5.1",
"eslint": "^8.20.0", "eslint": "^8.25.0",
"eslint-plugin-jane": "^11.2.2", "eslint-plugin-prettier": "^4.0.0",
"jest": "^27.0.6", "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", "node-fetch": "^2.6.1",
"prettier": "^2.7.1", "prettier": "^2.7.1",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"ts-jest": "^27.0.3", "ts-jest": "^27.0.3",
"typescript": "^4.6.4" "typescript": "^4.8.4"
} }
} }

View file

@ -1,7 +1,7 @@
jest.setTimeout(180000); jest.setTimeout(180000);
import {None, Some} from '@sniptt/monads'; import { None, Some } from "@sniptt/monads";
import { CommentView } from 'lemmy-js-client'; import { CommentView } from "lemmy-js-client";
import { PostResponse } from 'lemmy-js-client'; import { PostResponse } from "lemmy-js-client";
import { import {
alpha, alpha,
@ -31,7 +31,7 @@ import {
getComments, getComments,
getCommentParentId, getCommentParentId,
resolveCommunity, resolveCommunity,
} from './shared'; } from "./shared";
let postRes: PostResponse; let postRes: PostResponse;
@ -41,10 +41,7 @@ beforeAll(async () => {
await followBeta(alpha); await followBeta(alpha);
await followBeta(gamma); await followBeta(gamma);
let betaCommunity = (await resolveBetaCommunity(alpha)).community; let betaCommunity = (await resolveBetaCommunity(alpha)).community;
postRes = await createPost( postRes = await createPost(alpha, betaCommunity.unwrap().community.id);
alpha,
betaCommunity.unwrap().community.id
);
}); });
afterAll(async () => { afterAll(async () => {
@ -65,7 +62,7 @@ function assertCommentFederation(
expect(commentOne.comment.removed).toBe(commentOne.comment.removed); 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); let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
expect(commentRes.comment_view.comment.content).toBeDefined(); expect(commentRes.comment_view.comment.content).toBeDefined();
expect(commentRes.comment_view.community.local).toBe(false); expect(commentRes.comment_view.community.local).toBe(false);
@ -73,7 +70,9 @@ test('Create a comment', async () => {
expect(commentRes.comment_view.counts.score).toBe(1); expect(commentRes.comment_view.counts.score).toBe(1);
// Make sure that comment is liked on beta // 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).toBeDefined();
expect(betaComment.community.local).toBe(true); expect(betaComment.community.local).toBe(true);
expect(betaComment.creator.local).toBe(false); expect(betaComment.creator.local).toBe(false);
@ -81,15 +80,17 @@ test('Create a comment', async () => {
assertCommentFederation(betaComment, commentRes.comment_view); assertCommentFederation(betaComment, commentRes.comment_view);
}); });
test('Create a comment in a non-existent post', async () => { test("Create a comment in a non-existent post", async () => {
let commentRes = await createComment(alpha, -1, None) as any; let commentRes = (await createComment(alpha, -1, None)) as any;
expect(commentRes.error).toBe('couldnt_find_post'); 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); let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
// Federate the comment first // 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); assertCommentFederation(betaComment.unwrap(), commentRes.comment_view);
let updateCommentRes = await editComment( let updateCommentRes = await editComment(
@ -97,23 +98,19 @@ test('Update a comment', async () => {
commentRes.comment_view.comment.id commentRes.comment_view.comment.id
); );
expect(updateCommentRes.comment_view.comment.content).toBe( 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.community.local).toBe(false);
expect(updateCommentRes.comment_view.creator.local).toBe(true); expect(updateCommentRes.comment_view.creator.local).toBe(true);
// Make sure that post is updated on beta // Make sure that post is updated on beta
let betaCommentUpdated = (await resolveComment( let betaCommentUpdated = (
beta, await resolveComment(beta, commentRes.comment_view.comment)
commentRes.comment_view.comment ).comment.unwrap();
)).comment.unwrap(); assertCommentFederation(betaCommentUpdated, updateCommentRes.comment_view);
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 commentRes = await createComment(alpha, postRes.post_view.post.id, None);
let deleteCommentRes = await deleteComment( let deleteCommentRes = await deleteComment(
@ -125,8 +122,11 @@ test('Delete a comment', async () => {
expect(deleteCommentRes.comment_view.comment.content).toBe(""); expect(deleteCommentRes.comment_view.comment.content).toBe("");
// Make sure that comment is undefined on beta // Make sure that comment is undefined on beta
let betaCommentRes = await resolveComment(beta, commentRes.comment_view.comment) as any; let betaCommentRes = (await resolveComment(
expect(betaCommentRes.error).toBe('couldnt_find_object'); beta,
commentRes.comment_view.comment
)) as any;
expect(betaCommentRes.error).toBe("couldnt_find_object");
let undeleteCommentRes = await deleteComment( let undeleteCommentRes = await deleteComment(
alpha, alpha,
@ -136,15 +136,14 @@ test('Delete a comment', async () => {
expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false); expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false);
// Make sure that comment is undeleted on beta // 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); expect(betaComment2.comment.deleted).toBe(false);
assertCommentFederation( assertCommentFederation(betaComment2, undeleteCommentRes.comment_view);
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); let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
// Get the id for beta // 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(""); 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) // 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); expect(refetchedPostComments.comments[0].comment.removed).toBe(true);
let unremoveCommentRes = await removeComment(beta, false, betaCommentId); let unremoveCommentRes = await removeComment(beta, false, betaCommentId);
expect(unremoveCommentRes.comment_view.comment.removed).toBe(false); expect(unremoveCommentRes.comment_view.comment.removed).toBe(false);
// Make sure that comment is unremoved on beta // Make sure that comment is unremoved on 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); expect(refetchedPostComments2.comments[0].comment.removed).toBe(false);
assertCommentFederation( assertCommentFederation(
refetchedPostComments2.comments[0], 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 alpha_user = await registerUser(alpha);
let newAlphaApi: API = { let newAlphaApi: API = {
client: alpha.client, client: alpha.client,
@ -186,11 +191,17 @@ test('Remove a comment from admin and community on different instance', async ()
newAlphaApi, newAlphaApi,
newCommunity.community_view.community.id 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(); expect(commentRes.comment_view.comment.content).toBeDefined();
// Beta searches that to cache it, then removes it // 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( let removeCommentRes = await removeComment(
beta, beta,
true, 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); expect(removeCommentRes.comment_view.comment.removed).toBe(true);
// Make sure its not removed on alpha // 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); 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 commentRes = await createComment(alpha, postRes.post_view.post.id, None);
let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment); let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment);
expect(unlike.comment_view.counts.score).toBe(0); expect(unlike.comment_view.counts.score).toBe(0);
// Make sure that post is unliked on beta // 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).toBeDefined();
expect(betaComment.community.local).toBe(true); expect(betaComment.community.local).toBe(true);
expect(betaComment.creator.local).toBe(false); expect(betaComment.creator.local).toBe(false);
expect(betaComment.counts.score).toBe(0); 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); let commentRes = await createComment(alpha, postRes.post_view.post.id, None);
// Find the comment on beta // 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); let like = await likeComment(beta, 1, betaComment.comment);
expect(like.comment_view.counts.score).toBe(2); 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); 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 // Create a comment on alpha, find it on beta
let commentRes = await createComment(alpha, postRes.post_view.post.id, None); 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 // 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.comment.content).toBeDefined();
expect(replyRes.comment_view.community.local).toBe(true); expect(replyRes.comment_view.community.local).toBe(true);
expect(replyRes.comment_view.creator.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); expect(replyRes.comment_view.counts.score).toBe(1);
// Make sure that comment is seen on alpha // 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 postComments = await getComments(alpha, postRes.post_view.post.id);
let alphaComment = postComments.comments[0]; let alphaComment = postComments.comments[0];
expect(alphaComment.comment.content).toBeDefined(); 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.community.local).toBe(false);
expect(alphaComment.creator.local).toBe(false); expect(alphaComment.creator.local).toBe(false);
expect(alphaComment.counts.score).toBe(1); expect(alphaComment.counts.score).toBe(1);
assertCommentFederation(alphaComment, replyRes.comment_view); assertCommentFederation(alphaComment, replyRes.comment_view);
}); });
test('Mention beta', async () => { test("Mention beta", async () => {
// Create a mention on alpha // 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 commentRes = await createComment(alpha, postRes.post_view.post.id, None);
let mentionRes = await createComment( let mentionRes = await createComment(
alpha, alpha,
@ -286,23 +313,29 @@ test('Mention beta', async () => {
expect(mentionsRes.mentions[0].counts.score).toBe(1); 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 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); 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 // 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); let alphaPost = await createPost(alpha, alphaCommunity.community.id);
expect(alphaPost.post_view.community.local).toBe(true); expect(alphaPost.post_view.community.local).toBe(true);
// Make sure gamma sees it // 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 = 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( let commentRes = await createComment(
gamma, gamma,
gammaPost.post.id, 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); expect(commentRes.comment_view.counts.score).toBe(1);
// Make sure alpha sees it // 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].comment.content).toBe(commentContent);
expect(alphaPostComments2.comments[0].community.local).toBe(true); expect(alphaPostComments2.comments[0].community.local).toBe(true);
expect(alphaPostComments2.comments[0].creator.local).toBe(false); expect(alphaPostComments2.comments[0].creator.local).toBe(false);
expect(alphaPostComments2.comments[0].counts.score).toBe(1); 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 // Make sure beta has mentions
let mentionsRes = await getMentions(beta); 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); // 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 // Alpha and gamma users follow beta community
let alphaFollow = await followBeta(alpha); let alphaFollow = await followBeta(alpha);
expect(alphaFollow.community_view.community.local).toBe(false); 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); let gammaFollow = await followBeta(gamma);
expect(gammaFollow.community_view.community.local).toBe(false); 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 // Create a post on beta
let betaPost = await createPost(beta, 2); let betaPost = await createPost(beta, 2);
expect(betaPost.post_view.community.local).toBe(true); expect(betaPost.post_view.community.local).toBe(true);
// Make sure gamma and alpha see it // 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(); 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(); expect(alphaPost.post).toBeDefined();
// The bug: gamma comments, and alpha should see it. // The bug: gamma comments, and alpha should see it.
let commentContent = 'Comment from gamma'; let commentContent = "Comment from gamma";
let commentRes = await createComment( let commentRes = await createComment(
gamma, gamma,
gammaPost.post.id, 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].community.local).toBe(false);
expect(alphaPostComments2.comments[0].creator.local).toBe(false); expect(alphaPostComments2.comments[0].creator.local).toBe(false);
expect(alphaPostComments2.comments[0].counts.score).toBe(1); 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(alpha);
await unfollowRemotes(gamma); 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 // Unfollow all remote communities
let site = await unfollowRemotes(alpha); let site = await unfollowRemotes(alpha);
expect( 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); let postRes = await createPost(beta, 2);
expect(postRes.post_view.post.name).toBeDefined(); 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( let parentCommentRes = await createComment(
beta, beta,
postRes.post_view.post.id, 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. // 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( let childCommentRes = await createComment(
beta, beta,
postRes.post_view.post.id, 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 // Follow beta again
let follow = await followBeta(alpha); let follow = await followBeta(alpha);
expect(follow.community_view.community.local).toBe(false); 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 // 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( let updateRes = await editComment(
beta, beta,
childCommentRes.comment_view.comment.id, childCommentRes.comment_view.comment.id,
updatedCommentContent updatedCommentContent
); );
expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent); expect(updateRes.comment_view.comment.content).toBe(
updatedCommentContent.unwrap()
);
// Get the post from alpha // 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 alphaPost = await getPost(alpha, alphaPostB.post.id);
let alphaPostComments = await getComments(alpha, alphaPostB.post.id); let alphaPostComments = await getComments(alpha, alphaPostB.post.id);
expect(alphaPost.post_view.post.name).toBeDefined(); expect(alphaPost.post_view.post.name).toBeDefined();
assertCommentFederation(alphaPostComments.comments[1], parentCommentRes.comment_view); assertCommentFederation(
assertCommentFederation(alphaPostComments.comments[0], updateRes.comment_view); 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.community.local).toBe(false);
expect(alphaPost.post_view.creator.local).toBe(false); expect(alphaPost.post_view.creator.local).toBe(false);
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });
test("Report a comment", async () => {
test('Report a comment', async () => {
let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap(); 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(); 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(); expect(commentRes).toBeDefined();
let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap().comment; let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap()
let alphaReport = (await reportComment(alpha, alphaComment.id, randomString(10))) .comment;
.comment_report_view.comment_report; 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).toBeDefined();
expect(betaReport.resolved).toBe(false); 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); expect(betaReport.reason).toBe(alphaReport.reason);
}); });
function N(gamma: API, id: number, N: any, commentContent: string) {
throw new Error('Function not implemented.');
}

View file

@ -1,5 +1,5 @@
jest.setTimeout(120000); jest.setTimeout(120000);
import { CommunityView } from 'lemmy-js-client'; import { CommunityView } from "lemmy-js-client";
import { import {
alpha, alpha,
@ -18,7 +18,7 @@ import {
createPost, createPost,
getPost, getPost,
resolvePost, resolvePost,
} from './shared'; } from "./shared";
beforeAll(async () => { beforeAll(async () => {
await setupLogins(); await setupLogins();
@ -34,8 +34,12 @@ function assertCommunityFederation(
expect(communityOne.community.description.unwrapOr("none")).toBe( expect(communityOne.community.description.unwrapOr("none")).toBe(
communityTwo.community.description.unwrapOr("none") communityTwo.community.description.unwrapOr("none")
); );
expect(communityOne.community.icon.unwrapOr("none")).toBe(communityTwo.community.icon.unwrapOr("none")); expect(communityOne.community.icon.unwrapOr("none")).toBe(
expect(communityOne.community.banner.unwrapOr("none")).toBe(communityTwo.community.banner.unwrapOr("none")); communityTwo.community.icon.unwrapOr("none")
);
expect(communityOne.community.banner.unwrapOr("none")).toBe(
communityTwo.community.banner.unwrapOr("none")
);
expect(communityOne.community.published).toBe( expect(communityOne.community.published).toBe(
communityTwo.community.published communityTwo.community.published
); );
@ -44,35 +48,35 @@ function assertCommunityFederation(
expect(communityOne.community.deleted).toBe(communityTwo.community.deleted); expect(communityOne.community.deleted).toBe(communityTwo.community.deleted);
} }
test('Create community', async () => { test("Create community", async () => {
let communityRes = await createCommunity(alpha); let communityRes = await createCommunity(alpha);
expect(communityRes.community_view.community.name).toBeDefined(); expect(communityRes.community_view.community.name).toBeDefined();
// A dupe check // A dupe check
let prevName = communityRes.community_view.community.name; let prevName = communityRes.community_view.community.name;
let communityRes2: any = await createCommunity(alpha, prevName); 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 // Cache the community on beta, make sure it has the other fields
let searchShort = `!${prevName}@lemmy-alpha:8541`; 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); assertCommunityFederation(betaCommunity, communityRes.community_view);
}); });
test('Delete community', async () => { test("Delete community", async () => {
let communityRes = await createCommunity(beta); let communityRes = await createCommunity(beta);
// Cache the community on Alpha // Cache the community on Alpha
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; 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); assertCommunityFederation(alphaCommunity, communityRes.community_view);
// Follow the community from alpha // Follow the community from alpha
let follow = await followCommunity( let follow = await followCommunity(alpha, true, alphaCommunity.community.id);
alpha,
true,
alphaCommunity.community.id
);
// Make sure the follow response went through // Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false); expect(follow.community_view.community.local).toBe(false);
@ -83,7 +87,9 @@ test('Delete community', async () => {
communityRes.community_view.community.id communityRes.community_view.community.id
); );
expect(deleteCommunityRes.community_view.community.deleted).toBe(true); 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 // Make sure it got deleted on A
let communityOnAlphaDeleted = await getCommunity( 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); let communityRes = await createCommunity(beta);
// Cache the community on Alpha // Cache the community on Alpha
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; 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); assertCommunityFederation(alphaCommunity, communityRes.community_view);
// Follow the community from alpha // Follow the community from alpha
let follow = await followCommunity( let follow = await followCommunity(alpha, true, alphaCommunity.community.id);
alpha,
true,
alphaCommunity.community.id
);
// Make sure the follow response went through // Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false); expect(follow.community_view.community.local).toBe(false);
@ -134,7 +138,9 @@ test('Remove community', async () => {
communityRes.community_view.community.id communityRes.community_view.community.id
); );
expect(removeCommunityRes.community_view.community.removed).toBe(true); 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 // Make sure it got Removed on A
let communityOnAlphaRemoved = await getCommunity( 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); let communityRes = await createCommunity(beta);
expect(communityRes.community_view.community.name).toBeDefined(); expect(communityRes.community_view.community.name).toBeDefined();
let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; 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); 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 // create a community on alpha
let communityRes = (await createCommunity(alpha)).community_view; let communityRes = (await createCommunity(alpha)).community_view;
expect(communityRes.community.name).toBeDefined(); expect(communityRes.community.name).toBeDefined();
// gamma follows community and posts in it // gamma follows community and posts in it
let gammaCommunity = (await resolveCommunity(gamma, communityRes.community.actor_id)).community.unwrap(); let gammaCommunity = (
let gammaFollow = (await followCommunity(gamma, true, gammaCommunity.community.id)); 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"); 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.post.id).toBeDefined();
expect(gammaPost.creator_banned_from_community).toBe(false); expect(gammaPost.creator_banned_from_community).toBe(false);
// admin of beta decides to ban gamma from community // admin of beta decides to ban gamma from community
let betaCommunity = (await resolveCommunity(beta, communityRes.community.actor_id)).community.unwrap(); let betaCommunity = (
let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view.person; await resolveCommunity(beta, communityRes.community.actor_id)
let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.actor_id)).person.unwrap(); ).community.unwrap();
let banRes = (await banPersonFromCommunity(beta, bannedUserInfo2.person.id, betaCommunity.community.id, true, true)); let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view
console.log(banRes); .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); expect(banRes.banned).toBe(true);
// ban doesnt federate to community's origin instance alpha // 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); expect(alphaPost.creator_banned_from_community).toBe(false);
// and neither to gamma // 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); expect(gammaPost2.post_view.creator_banned_from_community).toBe(false);
}); });

View file

@ -1,5 +1,6 @@
jest.setTimeout(120000); jest.setTimeout(120000);
import {SubscribedType} from 'lemmy-js-client'; import { SubscribedType } from "lemmy-js-client";
import { import {
alpha, alpha,
setupLogins, setupLogins,
@ -7,8 +8,7 @@ import {
followCommunity, followCommunity,
unfollowRemotes, unfollowRemotes,
getSite, getSite,
delay, } from "./shared";
} from './shared';
beforeAll(async () => { beforeAll(async () => {
await setupLogins(); await setupLogins();
@ -18,24 +18,20 @@ afterAll(async () => {
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });
test('Follow federated community', async () => { test("Follow federated community", async () => {
let betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap(); let betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap();
let follow = await followCommunity( let follow = await followCommunity(alpha, true, betaCommunity.community.id);
alpha,
true,
betaCommunity.community.id
);
// Make sure the follow response went through // Make sure the follow response went through
expect(follow.community_view.community.local).toBe(false); 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); expect(follow.community_view.subscribed).toBe(SubscribedType.Subscribed);
// Check it from local // Check it from local
let site = await getSite(alpha); let site = await getSite(alpha);
let remoteCommunityId = site.my_user.unwrap().follows.find( let remoteCommunityId = site.my_user
c => c.community.local == false .unwrap()
).community.id; .follows.find(c => c.community.local == false).community.id;
expect(remoteCommunityId).toBeDefined(); expect(remoteCommunityId).toBeDefined();
expect(site.my_user.unwrap().follows.length).toBe(2); expect(site.my_user.unwrap().follows.length).toBe(2);

View file

@ -1,6 +1,7 @@
jest.setTimeout(120000); jest.setTimeout(120000);
import {None} from '@sniptt/monads'; import { None } from "@sniptt/monads";
import { PostView, CommunityView } from 'lemmy-js-client'; import { PostView, CommunityView } from "lemmy-js-client";
import { import {
alpha, alpha,
beta, beta,
@ -33,8 +34,8 @@ import {
API, API,
getSite, getSite,
unfollows, unfollows,
resolveCommunity resolveCommunity,
} from './shared'; } from "./shared";
let betaCommunity: CommunityView; let betaCommunity: CommunityView;
@ -52,12 +53,22 @@ afterAll(async () => {
function assertPostFederation(postOne: PostView, postTwo: PostView) { function assertPostFederation(postOne: PostView, postTwo: PostView) {
expect(postOne.post.ap_id).toBe(postTwo.post.ap_id); expect(postOne.post.ap_id).toBe(postTwo.post.ap_id);
expect(postOne.post.name).toBe(postTwo.post.name); expect(postOne.post.name).toBe(postTwo.post.name);
expect(postOne.post.body.unwrapOr("none")).toBe(postTwo.post.body.unwrapOr("none")); expect(postOne.post.body.unwrapOr("none")).toBe(
expect(postOne.post.url.unwrapOr("none")).toBe(postTwo.post.url.unwrapOr("none")); 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.nsfw).toBe(postTwo.post.nsfw);
expect(postOne.post.embed_title.unwrapOr("none")).toBe(postTwo.post.embed_title.unwrapOr("none")); expect(postOne.post.embed_title.unwrapOr("none")).toBe(
expect(postOne.post.embed_description.unwrapOr("none")).toBe(postTwo.post.embed_description.unwrapOr("none")); postTwo.post.embed_title.unwrapOr("none")
expect(postOne.post.embed_html.unwrapOr("none")).toBe(postTwo.post.embed_html.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.post.published).toBe(postTwo.post.published);
expect(postOne.community.actor_id).toBe(postTwo.community.actor_id); expect(postOne.community.actor_id).toBe(postTwo.community.actor_id);
expect(postOne.post.locked).toBe(postTwo.post.locked); 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); 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); let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); expect(postRes.post_view.post).toBeDefined();
expect(postRes.post_view.community.local).toBe(false); 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); expect(postRes.post_view.counts.score).toBe(1);
// Make sure that post is liked on beta // 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).toBeDefined();
expect(betaPost.community.local).toBe(true); 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 // Delta only follows beta, so it should not see an alpha ap_id
let deltaPost = (await resolvePost(delta, postRes.post_view.post)).post; 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 // Epsilon has alpha blocked, it should not see the alpha post
let epsilonPost = (await resolvePost(epsilon, postRes.post_view.post)).post; let epsilonPost = (await resolvePost(epsilon, postRes.post_view.post)).post;
expect(epsilonPost.isNone()).toBe(true); expect(epsilonPost.isNone()).toBe(true);
}); });
test('Create a post in a non-existent community', async () => { test("Create a post in a non-existent community", async () => {
let postRes = await createPost(alpha, -2) as any; let postRes = (await createPost(alpha, -2)) as any;
expect(postRes.error).toBe('couldnt_find_community'); 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 postRes = await createPost(alpha, betaCommunity.community.id);
let unlike = await likePost(alpha, 0, postRes.post_view.post); let unlike = await likePost(alpha, 0, postRes.post_view.post);
expect(unlike.post_view.counts.score).toBe(0); 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); expect(unlike2.post_view.counts.score).toBe(0);
// Make sure that post is unliked on beta // 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).toBeDefined();
expect(betaPost.community.local).toBe(true); expect(betaPost.community.local).toBe(true);
expect(betaPost.creator.local).toBe(false); expect(betaPost.creator.local).toBe(false);
@ -113,36 +128,42 @@ test('Unlike a post', async () => {
assertPostFederation(betaPost, postRes.post_view); assertPostFederation(betaPost, postRes.post_view);
}); });
test('Update a post', async () => { test("Update a post", async () => {
let postRes = await createPost(alpha, betaCommunity.community.id); 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); let updatedPost = await editPost(alpha, postRes.post_view.post);
expect(updatedPost.post_view.post.name).toBe(updatedName); expect(updatedPost.post_view.post.name).toBe(updatedName);
expect(updatedPost.post_view.community.local).toBe(false); expect(updatedPost.post_view.community.local).toBe(false);
expect(updatedPost.post_view.creator.local).toBe(true); expect(updatedPost.post_view.creator.local).toBe(true);
// Make sure that post is updated on beta // 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.community.local).toBe(true);
expect(betaPost.creator.local).toBe(false); expect(betaPost.creator.local).toBe(false);
expect(betaPost.post.name).toBe(updatedName); expect(betaPost.post.name).toBe(updatedName);
assertPostFederation(betaPost, updatedPost.post_view); assertPostFederation(betaPost, updatedPost.post_view);
// Make sure lemmy beta cannot update the post // Make sure lemmy beta cannot update the post
let updatedPostBeta = await editPost(beta, betaPost.post) as any; let updatedPostBeta = (await editPost(beta, betaPost.post)) as any;
expect(updatedPostBeta.error).toBe('no_post_edit_allowed'); 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 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); let stickiedPostRes = await stickyPost(beta, true, betaPost1.post);
expect(stickiedPostRes.post_view.post.stickied).toBe(true); expect(stickiedPostRes.post_view.post.stickied).toBe(true);
// Make sure that post is stickied on beta // 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.community.local).toBe(true);
expect(betaPost.creator.local).toBe(false); expect(betaPost.creator.local).toBe(false);
expect(betaPost.post.stickied).toBe(true); expect(betaPost.post.stickied).toBe(true);
@ -152,25 +173,33 @@ test('Sticky a post', async () => {
expect(unstickiedPost.post_view.post.stickied).toBe(false); expect(unstickiedPost.post_view.post.stickied).toBe(false);
// Make sure that post is unstickied on beta // 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.community.local).toBe(true);
expect(betaPost2.creator.local).toBe(false); expect(betaPost2.creator.local).toBe(false);
expect(betaPost2.post.stickied).toBe(false); expect(betaPost2.post.stickied).toBe(false);
// Make sure that gamma cannot sticky the post on beta // 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 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(gammaTrySticky.post_view.post.stickied).toBe(true);
expect(betaPost3.post.stickied).toBe(false); expect(betaPost3.post.stickied).toBe(false);
}); });
test('Lock a post', async () => { test("Lock a post", async () => {
await followCommunity(alpha, true, betaCommunity.community.id); await followCommunity(alpha, true, betaCommunity.community.id);
let postRes = await createPost(alpha, betaCommunity.community.id); let postRes = await createPost(alpha, betaCommunity.community.id);
// Lock the post // 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); let lockedPostRes = await lockPost(beta, true, betaPost1.post);
expect(lockedPostRes.post_view.post.locked).toBe(true); 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 // Try to make a new comment there, on alpha
let comment: any = await createComment(alpha, alphaPost1.post.id, None); let comment: any = await createComment(alpha, alphaPost1.post.id, None);
expect(comment['error']).toBe('locked'); expect(comment["error"]).toBe("locked");
// Unlock a post // Unlock a post
let unlockedPost = await lockPost(beta, false, betaPost1.post); let unlockedPost = await lockPost(beta, false, betaPost1.post);
@ -199,7 +228,7 @@ test('Lock a post', async () => {
expect(commentAlpha).toBeDefined(); expect(commentAlpha).toBeDefined();
}); });
test('Delete a post', async () => { test("Delete a post", async () => {
let postRes = await createPost(alpha, betaCommunity.community.id); let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); expect(postRes.post_view.post).toBeDefined();
@ -217,26 +246,38 @@ test('Delete a post', async () => {
expect(undeletedPost.post_view.post.deleted).toBe(false); expect(undeletedPost.post_view.post.deleted).toBe(false);
// Make sure lemmy beta sees post is undeleted // 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); expect(betaPost2.post.deleted).toBe(false);
assertPostFederation(betaPost2, undeletedPost.post_view); assertPostFederation(betaPost2, undeletedPost.post_view);
// Make sure lemmy beta cannot delete the post // Make sure lemmy beta cannot delete the post
let deletedPostBeta = await deletePost(beta, true, betaPost2.post) as any; let deletedPostBeta = (await deletePost(beta, true, betaPost2.post)) as any;
expect(deletedPostBeta.error).toStrictEqual('no_post_edit_allowed'); expect(deletedPostBeta.error).toStrictEqual("no_post_edit_allowed");
}); });
test('Remove a post from admin and community on different instance', async () => { test("Remove a post from admin and community on different instance", async () => {
let gammaCommunity = await resolveCommunity(gamma, betaCommunity.community.actor_id); let gammaCommunity = await resolveCommunity(
let postRes = await createPost(gamma, gammaCommunity.community.unwrap().community.id); 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); let removedPost = await removePost(alpha, true, alphaPost.post);
expect(removedPost.post_view.post.removed).toBe(true); expect(removedPost.post_view.post.removed).toBe(true);
expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name); expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name);
// Make sure lemmy beta sees post is NOT removed // 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); expect(betaPost.post.removed).toBe(false);
// Undelete // 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); expect(undeletedPost.post_view.post.removed).toBe(false);
// Make sure lemmy beta sees post is undeleted // 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); expect(betaPost2.post.removed).toBe(false);
assertPostFederation(betaPost2, undeletedPost.post_view); 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); await followBeta(alpha);
let postRes = await createPost(alpha, betaCommunity.community.id); let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); expect(postRes.post_view.post).toBeDefined();
@ -279,26 +322,31 @@ test('Remove a post from admin and community on same instance', async () => {
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });
test('Search for a post', async () => { test("Search for a post", async () => {
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
let postRes = await createPost(alpha, betaCommunity.community.id); let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); 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(); 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 // create a test user
let alphaUserJwt = await registerUser(alpha); let alphaUserJwt = await registerUser(alpha);
expect(alphaUserJwt).toBeDefined(); expect(alphaUserJwt).toBeDefined();
let alpha_user: API = { let alpha_user: API = {
client: alpha.client, client: alpha.client,
auth: alphaUserJwt.jwt, 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(); expect(alphaUserActorId).toBeDefined();
let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId)).person.unwrap(); let alphaPerson = (
await resolvePerson(alpha_user, alphaUserActorId)
).person.unwrap();
expect(alphaPerson).toBeDefined(); expect(alphaPerson).toBeDefined();
// alpha makes post in beta community, it federates to beta instance // 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(); expect(searchBeta1.posts[0]).toBeDefined();
// ban alpha from its instance // 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); expect(banAlpha.banned).toBe(true);
// alpha ban should be federated to beta // alpha ban should be federated to beta
@ -319,7 +372,12 @@ test('Enforce site ban for federated user', async () => {
expect(searchBeta2.posts[0]).toBeUndefined(); expect(searchBeta2.posts[0]).toBeUndefined();
// Unban alpha // 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); expect(unBanAlpha.banned).toBe(false);
// alpha makes new post in beta community, it federates // 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); let searchBeta3 = await searchPostLocal(beta, postRes2.post_view.post);
expect(searchBeta3.posts[0]).toBeDefined(); 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); 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 alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`;
let alphaPerson = (await resolvePerson(beta, alphaShortname)).person.unwrap(); let alphaPerson = (await resolvePerson(beta, alphaShortname)).person.unwrap();
expect(alphaPerson).toBeDefined(); expect(alphaPerson).toBeDefined();
@ -342,7 +400,13 @@ test('Enforce community ban for federated user', async () => {
expect(searchBeta1.posts[0]).toBeDefined(); expect(searchBeta1.posts[0]).toBeDefined();
// ban alpha from beta community // 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); expect(banAlpha.banned).toBe(true);
// ensure that the post by alpha got removed // 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(); 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); let postRes = await createPost(alpha, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); 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(); expect(betaPost.post.name).toBeDefined();
}); });
test('Report a post', async () => { test("Report a post", async () => {
let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap(); let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap();
let postRes = await createPost(beta, betaCommunity.community.id); let postRes = await createPost(beta, betaCommunity.community.id);
expect(postRes.post_view.post).toBeDefined(); expect(postRes.post_view.post).toBeDefined();
let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap(); let alphaPost = (
let alphaReport = (await reportPost(alpha, alphaPost.post.id, randomString(10))) await resolvePost(alpha, postRes.post_view.post)
.post_report_view.post_report; ).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; let betaReport = (await listPostReports(beta)).post_reports[0].post_report;
expect(betaReport).toBeDefined(); expect(betaReport).toBeDefined();
expect(betaReport.resolved).toBe(false); expect(betaReport.resolved).toBe(false);
expect(betaReport.original_post_name).toBe(alphaReport.original_post_name); 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_url.unwrapOr("none")).toBe(
expect(betaReport.original_post_body.unwrapOr("none")).toBe(alphaReport.original_post_body.unwrapOr("none")); 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); expect(betaReport.reason).toBe(alphaReport.reason);
}); });

View file

@ -9,7 +9,7 @@ import {
listPrivateMessages, listPrivateMessages,
deletePrivateMessage, deletePrivateMessage,
unfollowRemotes, unfollowRemotes,
} from './shared'; } from "./shared";
let recipient_id: number; let recipient_id: number;
@ -23,7 +23,7 @@ afterAll(async () => {
await unfollowRemotes(alpha); await unfollowRemotes(alpha);
}); });
test('Create a private message', async () => { test("Create a private message", async () => {
let pmRes = await createPrivateMessage(alpha, recipient_id); let pmRes = await createPrivateMessage(alpha, recipient_id);
expect(pmRes.private_message_view.private_message.content).toBeDefined(); expect(pmRes.private_message_view.private_message.content).toBeDefined();
expect(pmRes.private_message_view.private_message.local).toBe(true); 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); expect(betaPms.private_messages[0].recipient.local).toBe(true);
}); });
test('Update a private message', async () => { test("Update a private message", async () => {
let updatedContent = 'A jest test federated private message edited'; let updatedContent = "A jest test federated private message edited";
let pmRes = await createPrivateMessage(alpha, recipient_id); let pmRes = await createPrivateMessage(alpha, recipient_id);
let pmUpdated = await editPrivateMessage( 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 pmRes = await createPrivateMessage(alpha, recipient_id);
let betaPms1 = await listPrivateMessages(beta); let betaPms1 = await listPrivateMessages(beta);
let deletedPmRes = await deletePrivateMessage( let deletedPmRes = await deletePrivateMessage(

View file

@ -1,4 +1,4 @@
import {None, Some, Option} from '@sniptt/monads'; import { None, Some, Option } from "@sniptt/monads";
import { import {
Login, Login,
LoginResponse, LoginResponse,
@ -63,8 +63,8 @@ import {
EditSite, EditSite,
CommentSortType, CommentSortType,
GetComments, GetComments,
GetCommentsResponse GetCommentsResponse,
} from 'lemmy-js-client'; } from "lemmy-js-client";
export interface API { export interface API {
client: LemmyHttp; client: LemmyHttp;
@ -72,59 +72,59 @@ export interface API {
} }
export let alpha: 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, auth: None,
}; };
export let beta: API = { export let beta: API = {
client: new LemmyHttp('http://127.0.0.1:8551'), client: new LemmyHttp("http://127.0.0.1:8551"),
auth: None, auth: None,
}; };
export let gamma: API = { export let gamma: API = {
client: new LemmyHttp('http://127.0.0.1:8561'), client: new LemmyHttp("http://127.0.0.1:8561"),
auth: None, auth: None,
}; };
export let delta: API = { export let delta: API = {
client: new LemmyHttp('http://127.0.0.1:8571'), client: new LemmyHttp("http://127.0.0.1:8571"),
auth: None, auth: None,
}; };
export let epsilon: API = { export let epsilon: API = {
client: new LemmyHttp('http://127.0.0.1:8581'), client: new LemmyHttp("http://127.0.0.1:8581"),
auth: None, auth: None,
}; };
const password = 'lemmylemmy' const password = "lemmylemmy";
export async function setupLogins() { export async function setupLogins() {
let formAlpha = new Login({ let formAlpha = new Login({
username_or_email: 'lemmy_alpha', username_or_email: "lemmy_alpha",
password, password,
}); });
let resAlpha = alpha.client.login(formAlpha); let resAlpha = alpha.client.login(formAlpha);
let formBeta = new Login({ let formBeta = new Login({
username_or_email: 'lemmy_beta', username_or_email: "lemmy_beta",
password, password,
}); });
let resBeta = beta.client.login(formBeta); let resBeta = beta.client.login(formBeta);
let formGamma = new Login({ let formGamma = new Login({
username_or_email: 'lemmy_gamma', username_or_email: "lemmy_gamma",
password, password,
}); });
let resGamma = gamma.client.login(formGamma); let resGamma = gamma.client.login(formGamma);
let formDelta = new Login({ let formDelta = new Login({
username_or_email: 'lemmy_delta', username_or_email: "lemmy_delta",
password, password,
}); });
let resDelta = delta.client.login(formDelta); let resDelta = delta.client.login(formDelta);
let formEpsilon = new Login({ let formEpsilon = new Login({
username_or_email: 'lemmy_epsilon', username_or_email: "lemmy_epsilon",
password, password,
}); });
let resEpsilon = epsilon.client.login(formEpsilon); 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 // Registration applications are now enabled by default, need to disable them
let editSiteForm = new EditSite({ let editSiteForm = new EditSite({
require_application: Some(false),
federation_debug: Some(true),
name: None, name: None,
sidebar: None, sidebar: None,
description: None, description: None,
@ -155,23 +157,74 @@ export async function setupLogins() {
enable_nsfw: None, enable_nsfw: None,
community_creation_admin_only: None, community_creation_admin_only: None,
require_email_verification: None, require_email_verification: None,
require_application: Some(false),
application_question: None, application_question: None,
private_instance: None, private_instance: None,
default_theme: None, default_theme: None,
legal_information: None,
default_post_listing_type: 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: "", auth: "",
}); });
// Set the blocks and auths for each
editSiteForm.auth = alpha.auth.unwrap(); editSiteForm.auth = alpha.auth.unwrap();
editSiteForm.allowed_instances = Some([
"lemmy-beta",
"lemmy-gamma",
"lemmy-delta",
"lemmy-epsilon",
]);
await alpha.client.editSite(editSiteForm); await alpha.client.editSite(editSiteForm);
editSiteForm.auth = beta.auth.unwrap(); editSiteForm.auth = beta.auth.unwrap();
editSiteForm.allowed_instances = Some([
"lemmy-alpha",
"lemmy-gamma",
"lemmy-delta",
"lemmy-epsilon",
]);
await beta.client.editSite(editSiteForm); await beta.client.editSite(editSiteForm);
editSiteForm.auth = gamma.auth.unwrap(); editSiteForm.auth = gamma.auth.unwrap();
editSiteForm.allowed_instances = Some([
"lemmy-alpha",
"lemmy-beta",
"lemmy-delta",
"lemmy-epsilon",
]);
await gamma.client.editSite(editSiteForm); await gamma.client.editSite(editSiteForm);
editSiteForm.allowed_instances = Some(["lemmy-beta"]);
editSiteForm.auth = delta.auth.unwrap(); editSiteForm.auth = delta.auth.unwrap();
await delta.client.editSite(editSiteForm); await delta.client.editSite(editSiteForm);
editSiteForm.auth = epsilon.auth.unwrap(); editSiteForm.auth = epsilon.auth.unwrap();
editSiteForm.allowed_instances = Some([]);
editSiteForm.blocked_instances = Some(["lemmy-alpha"]);
await epsilon.client.editSite(editSiteForm); await epsilon.client.editSite(editSiteForm);
// Create the main alpha/beta communities // Create the main alpha/beta communities
@ -185,7 +238,7 @@ export async function createPost(
): Promise<PostResponse> { ): Promise<PostResponse> {
let name = randomString(5); let name = randomString(5);
let body = Some(randomString(10)); let body = Some(randomString(10));
let url = Some('https://google.com/'); let url = Some("https://google.com/");
let form = new CreatePost({ let form = new CreatePost({
name, name,
url, url,
@ -194,12 +247,13 @@ export async function createPost(
community_id, community_id,
nsfw: None, nsfw: None,
honeypot: None, honeypot: None,
language_id: None,
}); });
return api.client.createPost(form); return api.client.createPost(form);
} }
export async function editPost(api: API, post: Post): Promise<PostResponse> { 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({ let form = new EditPost({
name, name,
post_id: post.id, post_id: post.id,
@ -207,6 +261,7 @@ export async function editPost(api: API, post: Post): Promise<PostResponse> {
nsfw: None, nsfw: None,
url: None, url: None,
body: None, body: None,
language_id: None,
}); });
return api.client.editPost(form); return api.client.editPost(form);
} }
@ -342,7 +397,7 @@ export async function resolveBetaCommunity(
): Promise<ResolveObjectResponse> { ): Promise<ResolveObjectResponse> {
// Use short-hand search url // Use short-hand search url
let form = new ResolveObject({ let form = new ResolveObject({
q: '!main@lemmy-beta:8551', q: "!main@lemmy-beta:8551",
auth: api.auth, auth: api.auth,
}); });
return api.client.resolveObject(form); return api.client.resolveObject(form);
@ -415,7 +470,7 @@ export async function followCommunity(
let form = new FollowCommunity({ let form = new FollowCommunity({
community_id, community_id,
follow, follow,
auth: api.auth.unwrap() auth: api.auth.unwrap(),
}); });
return api.client.followCommunity(form); return api.client.followCommunity(form);
} }
@ -428,7 +483,7 @@ export async function likePost(
let form = new CreatePostLike({ let form = new CreatePostLike({
post_id: post.id, post_id: post.id,
score: score, score: score,
auth: api.auth.unwrap() auth: api.auth.unwrap(),
}); });
return api.client.likePost(form); return api.client.likePost(form);
@ -438,13 +493,14 @@ export async function createComment(
api: API, api: API,
post_id: number, post_id: number,
parent_id: Option<number>, parent_id: Option<number>,
content = 'a jest test comment' content = "a jest test comment"
): Promise<CommentResponse> { ): Promise<CommentResponse> {
let form = new CreateComment({ let form = new CreateComment({
content, content,
post_id, post_id,
parent_id, parent_id,
form_id: None, form_id: None,
language_id: None,
auth: api.auth.unwrap(), auth: api.auth.unwrap(),
}); });
return api.client.createComment(form); return api.client.createComment(form);
@ -453,13 +509,15 @@ export async function createComment(
export async function editComment( export async function editComment(
api: API, api: API,
comment_id: number, comment_id: number,
content = 'A jest test federated comment update' content = Some("A jest test federated comment update")
): Promise<CommentResponse> { ): Promise<CommentResponse> {
let form = new EditComment({ let form = new EditComment({
content, content,
comment_id, comment_id,
form_id: None, form_id: None,
auth: api.auth.unwrap() language_id: None,
distinguished: None,
auth: api.auth.unwrap(),
}); });
return api.client.editComment(form); return api.client.editComment(form);
} }
@ -491,7 +549,9 @@ export async function removeComment(
return api.client.removeComment(form); 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({ let form = new GetPersonMentions({
sort: Some(CommentSortType.New), sort: Some(CommentSortType.New),
unread_only: Some(false), unread_only: Some(false),
@ -519,7 +579,7 @@ export async function createCommunity(
api: API, api: API,
name_: string = randomString(5) name_: string = randomString(5)
): Promise<CommunityResponse> { ): Promise<CommunityResponse> {
let description = Some('a sample description'); let description = Some("a sample description");
let form = new CreateCommunity({ let form = new CreateCommunity({
name: name_, name: name_,
title: name_, title: name_,
@ -577,7 +637,7 @@ export async function createPrivateMessage(
api: API, api: API,
recipient_id: number recipient_id: number
): Promise<PrivateMessageResponse> { ): Promise<PrivateMessageResponse> {
let content = 'A jest test federated private message'; let content = "A jest test federated private message";
let form = new CreatePrivateMessage({ let form = new CreatePrivateMessage({
content, content,
recipient_id, recipient_id,
@ -590,7 +650,7 @@ export async function editPrivateMessage(
api: API, api: API,
private_message_id: number private_message_id: number
): Promise<PrivateMessageResponse> { ): Promise<PrivateMessageResponse> {
let updatedContent = 'A jest test federated private message edited'; let updatedContent = "A jest test federated private message edited";
let form = new EditPrivateMessage({ let form = new EditPrivateMessage({
content: updatedContent, content: updatedContent,
private_message_id, private_message_id,
@ -630,18 +690,18 @@ export async function registerUser(
return api.client.register(form); return api.client.register(form);
} }
export async function saveUserSettingsBio( export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
api: API
): Promise<LoginResponse> {
let form = new SaveUserSettings({ let form = new SaveUserSettings({
show_nsfw: Some(true), show_nsfw: Some(true),
theme: Some('darkly'), theme: Some("darkly"),
default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Active)), default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Active)),
default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)), default_listing_type: Some(
lang: Some('en'), Object.keys(ListingType).indexOf(ListingType.All)
),
interface_language: Some("en"),
show_avatars: Some(true), show_avatars: Some(true),
send_notifications_to_email: Some(false), send_notifications_to_email: Some(false),
bio: Some('a changed bio'), bio: Some("a changed bio"),
avatar: None, avatar: None,
banner: None, banner: None,
display_name: None, display_name: None,
@ -652,6 +712,7 @@ export async function saveUserSettingsBio(
show_bot_accounts: None, show_bot_accounts: None,
show_new_post_notifs: None, show_new_post_notifs: None,
bot_account: None, bot_account: None,
discussion_languages: None,
auth: api.auth.unwrap(), auth: api.auth.unwrap(),
}); });
return saveUserSettings(api, form); return saveUserSettings(api, form);
@ -660,18 +721,20 @@ export async function saveUserSettingsBio(
export async function saveUserSettingsFederated( export async function saveUserSettingsFederated(
api: API api: API
): Promise<LoginResponse> { ): Promise<LoginResponse> {
let avatar = Some('https://image.flaticon.com/icons/png/512/35/35896.png'); 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 banner = Some("https://image.flaticon.com/icons/png/512/36/35896.png");
let bio = Some('a changed bio'); let bio = Some("a changed bio");
let form = new SaveUserSettings({ let form = new SaveUserSettings({
show_nsfw: Some(false), show_nsfw: Some(false),
theme: Some(''), theme: Some(""),
default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Hot)), default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Hot)),
default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)), default_listing_type: Some(
lang: Some(''), Object.keys(ListingType).indexOf(ListingType.All)
),
interface_language: Some(""),
avatar, avatar,
banner, banner,
display_name: Some('user321'), display_name: Some("user321"),
show_avatars: Some(false), show_avatars: Some(false),
send_notifications_to_email: Some(false), send_notifications_to_email: Some(false),
bio, bio,
@ -682,6 +745,7 @@ export async function saveUserSettingsFederated(
bot_account: None, bot_account: None,
show_bot_accounts: None, show_bot_accounts: None,
show_new_post_notifs: None, show_new_post_notifs: None,
discussion_languages: None,
auth: api.auth.unwrap(), auth: api.auth.unwrap(),
}); });
return await saveUserSettings(alpha, form); return await saveUserSettings(alpha, form);
@ -694,19 +758,15 @@ export async function saveUserSettings(
return api.client.saveUserSettings(form); return api.client.saveUserSettings(form);
} }
export async function deleteUser( export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
api: API
): Promise<DeleteAccountResponse> {
let form = new DeleteAccount({ let form = new DeleteAccount({
auth: api.auth.unwrap(), auth: api.auth.unwrap(),
password password,
}); });
return api.client.deleteAccount(form); return api.client.deleteAccount(form);
} }
export async function getSite( export async function getSite(api: API): Promise<GetSiteResponse> {
api: API
): Promise<GetSiteResponse> {
let form = new GetSite({ let form = new GetSite({
auth: api.auth, auth: api.auth,
}); });
@ -725,14 +785,12 @@ export async function listPrivateMessages(
return api.client.getPrivateMessages(form); return api.client.getPrivateMessages(form);
} }
export async function unfollowRemotes( export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
api: API
): Promise<GetSiteResponse> {
// Unfollow all remote communities // Unfollow all remote communities
let site = await getSite(api); let site = await getSite(api);
let remoteFollowed = site.my_user.unwrap().follows.filter( let remoteFollowed = site.my_user
c => c.community.local == false .unwrap()
); .follows.filter(c => c.community.local == false);
for (let cu of remoteFollowed) { for (let cu of remoteFollowed) {
await followCommunity(api, false, cu.community.id); await followCommunity(api, false, cu.community.id);
} }
@ -743,7 +801,11 @@ export async function unfollowRemotes(
export async function followBeta(api: API): Promise<CommunityResponse> { export async function followBeta(api: API): Promise<CommunityResponse> {
let betaCommunity = (await resolveBetaCommunity(api)).community; let betaCommunity = (await resolveBetaCommunity(api)).community;
if (betaCommunity.isSome()) { 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; return follow;
} else { } else {
return Promise.reject("no community worked"); return Promise.reject("no community worked");
@ -763,7 +825,9 @@ export async function reportPost(
return api.client.createPostReport(form); 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({ let form = new ListPostReports({
auth: api.auth.unwrap(), auth: api.auth.unwrap(),
page: None, page: None,
@ -787,7 +851,9 @@ export async function reportComment(
return api.client.createCommentReport(form); 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({ let form = new ListCommentReports({
page: None, page: None,
limit: None, limit: None,
@ -798,7 +864,7 @@ export async function listCommentReports(api: API): Promise<ListCommentReportsRe
return api.client.listCommentReports(form); return api.client.listCommentReports(form);
} }
export function delay(millis: number = 500) { export function delay(millis = 500) {
return new Promise(resolve => setTimeout(resolve, millis)); return new Promise(resolve => setTimeout(resolve, millis));
} }
@ -811,8 +877,9 @@ export function wrapper(form: any): string {
} }
export function randomString(length: number): string { export function randomString(length: number): string {
var result = ''; var result = "";
var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'; var characters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
var charactersLength = characters.length; var charactersLength = characters.length;
for (var i = 0; i < length; i++) { for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength)); result += characters.charAt(Math.floor(Math.random() * charactersLength));

View file

@ -1,8 +1,6 @@
jest.setTimeout(120000); jest.setTimeout(120000);
import {None} from '@sniptt/monads'; import { None } from "@sniptt/monads";
import { import { PersonViewSafe } from "lemmy-js-client";
PersonViewSafe,
} from 'lemmy-js-client';
import { import {
alpha, alpha,
@ -20,7 +18,7 @@ import {
resolveComment, resolveComment,
saveUserSettingsFederated, saveUserSettingsFederated,
setupLogins, setupLogins,
} from './shared'; } from "./shared";
beforeAll(async () => { beforeAll(async () => {
await setupLogins(); await setupLogins();
@ -28,59 +26,82 @@ beforeAll(async () => {
let apShortname: string; 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.name).toBe(userTwo.person.name);
expect(userOne.person.display_name.unwrapOr("none")).toBe(userTwo.person.display_name.unwrapOr("none")); expect(userOne.person.display_name.unwrapOr("none")).toBe(
expect(userOne.person.bio.unwrapOr("none")).toBe(userTwo.person.bio.unwrapOr("none")); 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.actor_id).toBe(userTwo.person.actor_id);
expect(userOne.person.avatar.unwrapOr("none")).toBe(userTwo.person.avatar.unwrapOr("none")); expect(userOne.person.avatar.unwrapOr("none")).toBe(
expect(userOne.person.banner.unwrapOr("none")).toBe(userTwo.person.banner.unwrapOr("none")); 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); expect(userOne.person.published).toBe(userTwo.person.published);
} }
test('Create user', async () => { test("Create user", async () => {
let userRes = await registerUser(alpha); let userRes = await registerUser(alpha);
expect(userRes.jwt).toBeDefined(); expect(userRes.jwt).toBeDefined();
alpha.auth = userRes.jwt; alpha.auth = userRes.jwt;
let site = await getSite(alpha); let site = await getSite(alpha);
expect(site.my_user).toBeDefined(); 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); await saveUserSettingsFederated(alpha);
let alphaPerson = (await resolvePerson(alpha, apShortname)).person.unwrap(); let alphaPerson = (await resolvePerson(alpha, apShortname)).person.unwrap();
let betaPerson = (await resolvePerson(beta, apShortname)).person.unwrap(); let betaPerson = (await resolvePerson(beta, apShortname)).person.unwrap();
assertUserFederation(alphaPerson, betaPerson); assertUserFederation(alphaPerson, betaPerson);
}); });
test('Delete user', async () => { test("Delete user", async () => {
let userRes = await registerUser(alpha); let userRes = await registerUser(alpha);
expect(userRes.jwt).toBeDefined(); expect(userRes.jwt).toBeDefined();
let user: API = { let user: API = {
client: alpha.client, client: alpha.client,
auth: userRes.jwt auth: userRes.jwt,
} };
// make a local post and comment // make a local post and comment
let alphaCommunity = (await resolveCommunity(user, '!main@lemmy-alpha:8541')).community.unwrap(); let alphaCommunity = (
let localPost = (await createPost(user, alphaCommunity.community.id)).post_view.post; await resolveCommunity(user, "!main@lemmy-alpha:8541")
).community.unwrap();
let localPost = (await createPost(user, alphaCommunity.community.id))
.post_view.post;
expect(localPost).toBeDefined(); 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(); expect(localComment).toBeDefined();
// make a remote post and comment // make a remote post and comment
let betaCommunity = (await resolveBetaCommunity(user)).community.unwrap(); 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(); 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(); expect(remoteComment).toBeDefined();
await deleteUser(user); await deleteUser(user);
expect((await resolvePost(alpha, localPost)).post.isNone()).toBe(true); expect((await resolvePost(alpha, localPost)).post.isNone()).toBe(true);
expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe(true) expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe(
expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true) true
expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe(true) );
expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true);
expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe(
true
);
}); });

File diff suppressed because it is too large Load diff

View file

@ -2,11 +2,4 @@
# https://join-lemmy.org/docs/en/administration/configuration.html # https://join-lemmy.org/docs/en/administration/configuration.html
{ {
hostname: lemmy-alpha 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?)
'''
} }

View file

@ -14,64 +14,7 @@
# Maximum number of active sql connections # Maximum number of active sql connections
pool_size: 5 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 # 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 image server configuration.
pictrs: { pictrs: {
# Address where pictrs is available (for image hosting) # Address where pictrs is available (for image hosting)
@ -79,12 +22,6 @@
# Set a custom pictrs API key. ( Required for deleting images ) # Set a custom pictrs API key. ( Required for deleting images )
api_key: "string" 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 sending configuration. All options except login/password are mandatory
email: { email: {
# Hostname and port of the smtp server # Hostname and port of the smtp server
@ -117,8 +54,4 @@
port: 8536 port: 8536
# Whether the site is available over TLS. Needs to be true for federation to work. # Whether the site is available over TLS. Needs to be true for federation to work.
tls_enabled: true 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
} }

View file

@ -16,6 +16,7 @@ use lemmy_db_schema::{
source::{ source::{
comment::{CommentLike, CommentLikeForm}, comment::{CommentLike, CommentLikeForm},
comment_reply::CommentReply, comment_reply::CommentReply,
local_site::LocalSite,
}, },
traits::Likeable, traits::Likeable,
}; };
@ -35,13 +36,14 @@ impl Perform for CreateCommentLike {
websocket_id: Option<ConnectionId>, websocket_id: Option<ConnectionId>,
) -> Result<CommentResponse, LemmyError> { ) -> Result<CommentResponse, LemmyError> {
let data: &CreateCommentLike = self; let data: &CreateCommentLike = self;
let local_site = blocking(context.pool(), LocalSite::read).await??;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let mut recipient_ids = Vec::<LocalUserId>::new(); let mut recipient_ids = Vec::<LocalUserId>::new();
// Don't do a downvote if site has downvotes disabled // 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 comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {

View file

@ -7,7 +7,10 @@ use lemmy_api_common::{
}; };
use lemmy_apub::protocol::activities::community::report::Report; use lemmy_apub::protocol::activities::community::report::Report;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::comment_report::{CommentReport, CommentReportForm}, source::{
comment_report::{CommentReport, CommentReportForm},
local_site::LocalSite,
},
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{CommentReportView, CommentView}; use lemmy_db_views::structs::{CommentReportView, CommentView};
@ -28,9 +31,10 @@ impl Perform for CreateCommentReport {
let data: &CreateCommentReport = self; let data: &CreateCommentReport = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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(); 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 person_id = local_user_view.person.id;
let comment_id = data.comment_id; let comment_id = data.comment_id;

View file

@ -7,11 +7,10 @@ use lemmy_api_common::{
use lemmy_apub::protocol::activities::community::update::UpdateCommunity; use lemmy_apub::protocol::activities::community::update::UpdateCommunity;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::{Community, CommunityForm}, community::{Community, CommunityUpdateForm},
moderator::{ModHideCommunity, ModHideCommunityForm}, moderator::{ModHideCommunity, ModHideCommunityForm},
}, },
traits::Crud, traits::Crud,
utils::naive_now,
}; };
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; 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?; get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let community_id = data.community_id; let community_form = CommunityUpdateForm::builder()
let read_community = blocking(context.pool(), move |conn| { .hidden(Some(data.hidden))
Community::read(conn, community_id) .build();
})
.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 mod_hide_community_form = ModHideCommunityForm { let mod_hide_community_form = ModHideCommunityForm {
community_id: data.community_id, community_id: data.community_id,

View file

@ -7,8 +7,10 @@ use lemmy_api_common::{
post::*, post::*,
private_message::*, private_message::*,
site::*, site::*,
utils::local_site_to_slur_regex,
websocket::*, websocket::*,
}; };
use lemmy_db_schema::source::local_site::LocalSite;
use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId}; use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId};
use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
use serde::Deserialize; use serde::Deserialize;
@ -227,8 +229,10 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String {
} }
/// Check size of report and remove whitespace /// Check size of report and remove whitespace
pub(crate) fn check_report_reason(reason: &str, context: &LemmyContext) -> Result<(), LemmyError> { pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> {
check_slurs(reason, &context.settings().slur_regex())?; let slur_regex = &local_site_to_slur_regex(local_site);
check_slurs(reason, slur_regex)?;
if reason.is_empty() { if reason.is_empty() {
return Err(LemmyError::from_message("report_reason_required")); return Err(LemmyError::from_message("report_reason_required"));
} }
@ -243,8 +247,9 @@ mod tests {
use lemmy_api_common::utils::check_validator_time; use lemmy_api_common::utils::check_validator_time;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
local_user::{LocalUser, LocalUserForm}, instance::Instance,
person::{Person, PersonForm}, local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonInsertForm},
secret::Secret, secret::Secret,
}, },
traits::Crud, traits::Crud,
@ -258,19 +263,20 @@ mod tests {
let secret = Secret::init(conn).unwrap(); let secret = Secret::init(conn).unwrap();
let settings = &SETTINGS.to_owned(); let settings = &SETTINGS.to_owned();
let new_person = PersonForm { let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap();
name: "Gerry9812".into(),
public_key: Some("pubkey".to_string()), let new_person = PersonInsertForm::builder()
..PersonForm::default() .name("Gerry9812".into())
}; .public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let inserted_person = Person::create(conn, &new_person).unwrap(); let inserted_person = Person::create(conn, &new_person).unwrap();
let local_user_form = LocalUserForm { let local_user_form = LocalUserInsertForm::builder()
person_id: Some(inserted_person.id), .person_id(inserted_person.id)
password_encrypted: Some("123456".to_string()), .password_encrypted("123456".to_string())
..LocalUserForm::default() .build();
};
let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap(); let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap();

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
moderator::{ModAdd, ModAddForm}, moderator::{ModAdd, ModAddForm},
person::Person, person::{Person, PersonUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -35,7 +35,11 @@ impl Perform for AddAdmin {
let added = data.added; let added = data.added;
let added_person_id = data.person_id; let added_person_id = data.person_id;
let added_admin = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?;

View file

@ -11,11 +11,11 @@ use lemmy_apub::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
moderator::{ModBan, ModBanForm}, moderator::{ModBan, ModBanForm},
person::Person, person::{Person, PersonUpdateForm},
site::Site,
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::SiteView;
use lemmy_db_views_actor::structs::PersonViewSafe; use lemmy_db_views_actor::structs::PersonViewSafe;
use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId}; use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId};
use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation}; use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation};
@ -41,10 +41,18 @@ impl Perform for BanPerson {
let banned_person_id = data.person_id; let banned_person_id = data.person_id;
let expires = data.expires.map(naive_from_unix); 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(), move |conn| {
let person = blocking(context.pool(), ban_person) Person::update(
.await? conn,
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; 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"))?;
// Remove their data if that's desired // Remove their data if that's desired
let remove_data = data.remove_data.unwrap_or(false); let remove_data = data.remove_data.unwrap_or(false);
@ -75,7 +83,12 @@ impl Perform for BanPerson {
}) })
.await??; .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 the action affects a local user, federate to other instances
if person.local { if person.local {
if ban { if ban {

View file

@ -2,8 +2,11 @@ use crate::{captcha_as_wav_base64, Perform};
use actix_web::web::Data; use actix_web::web::Data;
use captcha::{gen, Difficulty}; use captcha::{gen, Difficulty};
use chrono::Duration; use chrono::Duration;
use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse}; use lemmy_api_common::{
use lemmy_db_schema::utils::naive_now; 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_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{messages::CaptchaItem, LemmyContext}; use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
@ -17,13 +20,13 @@ impl Perform for GetCaptcha {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<Self::Response, LemmyError> { ) -> 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 }); 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, "easy" => Difficulty::Easy,
"hard" => Difficulty::Hard, "hard" => Difficulty::Hard,
_ => Difficulty::Medium, _ => Difficulty::Medium,

View file

@ -5,7 +5,7 @@ use lemmy_api_common::{
person::{Login, LoginResponse}, person::{Login, LoginResponse},
utils::{blocking, check_registration_application, check_user_valid}, 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_db_views::structs::LocalUserView;
use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId}; use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -22,6 +22,8 @@ impl Perform for Login {
) -> Result<LoginResponse, LemmyError> { ) -> Result<LoginResponse, LemmyError> {
let data: &Login = self; let data: &Login = self;
let local_site = blocking(context.pool(), LocalSite::read).await??;
// Fetch that username / email // Fetch that username / email
let username_or_email = data.username_or_email.clone(); let username_or_email = data.username_or_email.clone();
let local_user_view = blocking(context.pool(), move |conn| { let local_user_view = blocking(context.pool(), move |conn| {
@ -45,12 +47,11 @@ impl Perform for Login {
local_user_view.person.deleted, local_user_view.person.deleted,
)?; )?;
let site = blocking(context.pool(), Site::read_local).await??; if local_site.require_email_verification && !local_user_view.local_user.email_verified {
if site.require_email_verification && !local_user_view.local_user.email_verified {
return Err(LemmyError::from_message("email_not_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 // Return the jwt
Ok(LoginResponse { Ok(LoginResponse {

View file

@ -4,7 +4,10 @@ use lemmy_api_common::{
person::{MarkPersonMentionAsRead, PersonMentionResponse}, person::{MarkPersonMentionAsRead, PersonMentionResponse},
utils::{blocking, get_local_user_view_from_jwt}, 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_db_views_actor::structs::PersonMentionView;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -34,12 +37,12 @@ impl Perform for MarkPersonMentionAsRead {
} }
let person_mention_id = read_person_mention.id; let person_mention_id = read_person_mention.id;
let read = data.read; let read = Some(data.read);
let update_mention = blocking(context.pool(), move |conn| {
move |conn: &mut _| PersonMention::update_read(conn, person_mention_id, read); PersonMention::update(conn, person_mention_id, &PersonMentionUpdateForm { read })
blocking(context.pool(), update_mention) })
.await? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
let person_mention_id = read_person_mention.id; let person_mention_id = read_person_mention.id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -4,7 +4,10 @@ use lemmy_api_common::{
person::{CommentReplyResponse, MarkCommentReplyAsRead}, person::{CommentReplyResponse, MarkCommentReplyAsRead},
utils::{blocking, get_local_user_view_from_jwt}, 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_db_views_actor::structs::CommentReplyView;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -34,11 +37,12 @@ impl Perform for MarkCommentReplyAsRead {
} }
let comment_reply_id = read_comment_reply.id; let comment_reply_id = read_comment_reply.id;
let read = data.read; let read = Some(data.read);
let update_reply = move |conn: &mut _| CommentReply::update_read(conn, comment_reply_id, read); blocking(context.pool(), move |conn| {
blocking(context.pool(), update_reply) CommentReply::update(conn, comment_reply_id, &CommentReplyUpdateForm { read })
.await? })
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;
let comment_reply_id = read_comment_reply.id; let comment_reply_id = read_comment_reply.id;
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;

View file

@ -7,12 +7,12 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
actor_language::LocalUserLanguage, actor_language::LocalUserLanguage,
local_user::{LocalUser, LocalUserForm}, local_site::LocalSite,
person::{Person, PersonForm}, local_user::{LocalUser, LocalUserUpdateForm},
site::Site, person::{Person, PersonUpdateForm},
}, },
traits::Crud, 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::{ use lemmy_utils::{
claims::Claims, claims::Claims,
@ -35,6 +35,7 @@ impl Perform for SaveUserSettings {
let data: &SaveUserSettings = self; let data: &SaveUserSettings = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 avatar = diesel_option_overwrite_to_url(&data.avatar)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; 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 // When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value
if let Some(email) = &email { if let Some(email) = &email {
let site_fut = blocking(context.pool(), Site::read_local); if email.is_none() && local_site.require_email_verification {
if email.is_none() && site_fut.await??.require_email_verification {
return Err(LemmyError::from_message("email_required")); return Err(LemmyError::from_message("email_required"));
} }
} }
@ -71,7 +71,7 @@ impl Perform for SaveUserSettings {
if let Some(Some(display_name)) = &display_name { if let Some(Some(display_name)) = &display_name {
if !is_valid_display_name( if !is_valid_display_name(
display_name.trim(), display_name.trim(),
context.settings().actor_name_max_length, local_site.actor_name_max_length as usize,
) { ) {
return Err(LemmyError::from_message("invalid_username")); return Err(LemmyError::from_message("invalid_username"));
} }
@ -87,31 +87,15 @@ impl Perform for SaveUserSettings {
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
let default_listing_type = data.default_listing_type; let default_listing_type = data.default_listing_type;
let default_sort_type = data.default_sort_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 { let person_form = PersonUpdateForm::builder()
name: local_user_view.person.name, .display_name(display_name)
avatar, .bio(bio)
banner, .matrix_user_id(matrix_user_id)
inbox_url: None, .bot_account(bot_account)
display_name, .avatar(avatar)
published: None, .banner(banner)
updated: Some(naive_now()), .build();
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,
};
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
Person::update(conn, person_id, &person_form) Person::update(conn, person_id, &person_form)
@ -126,24 +110,20 @@ impl Perform for SaveUserSettings {
.await??; .await??;
} }
let local_user_form = LocalUserForm { let local_user_form = LocalUserUpdateForm::builder()
person_id: Some(person_id), .email(email)
email, .show_avatars(data.show_avatars)
password_encrypted: Some(password_encrypted), .show_read_posts(data.show_read_posts)
show_nsfw: data.show_nsfw, .show_new_post_notifs(data.show_new_post_notifs)
show_bot_accounts: data.show_bot_accounts, .send_notifications_to_email(data.send_notifications_to_email)
show_scores: data.show_scores, .show_nsfw(data.show_nsfw)
theme: data.theme.to_owned(), .show_bot_accounts(data.show_bot_accounts)
default_sort_type, .show_scores(data.show_scores)
default_listing_type, .default_sort_type(default_sort_type)
interface_language: data.interface_language.to_owned(), .default_listing_type(default_listing_type)
show_avatars: data.show_avatars, .theme(data.theme.to_owned())
show_read_posts: data.show_read_posts, .interface_language(data.interface_language.to_owned())
show_new_post_notifs: data.show_new_post_notifs, .build();
send_notifications_to_email: data.send_notifications_to_email,
email_verified: None,
accepted_application: None,
};
let local_user_res = blocking(context.pool(), move |conn| { let local_user_res = blocking(context.pool(), move |conn| {
LocalUser::update(conn, local_user_id, &local_user_form) LocalUser::update(conn, local_user_id, &local_user_form)

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
email_verification::EmailVerification, email_verification::EmailVerification,
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -31,13 +31,12 @@ impl Perform for VerifyEmail {
.await? .await?
.map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?; .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 // 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 // necessary in case email of an existing user was changed
email: Some(Some(verification.email)), .email(Some(Some(verification.email)))
..LocalUserForm::default() .build();
};
let local_user_id = verification.local_user_id; let local_user_id = verification.local_user_id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
LocalUser::update(conn, local_user_id, &form) LocalUser::update(conn, local_user_id, &form)

View file

@ -20,7 +20,10 @@ use lemmy_apub::{
}, },
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::post::{Post, PostLike, PostLikeForm}, source::{
local_site::LocalSite,
post::{Post, PostLike, PostLikeForm},
},
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -39,9 +42,10 @@ impl Perform for CreatePostLike {
let data: &CreatePostLike = self; let data: &CreatePostLike = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 // 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 // Check for a community ban
let post_id = data.post_id; let post_id = data.post_id;

View file

@ -17,7 +17,7 @@ use lemmy_apub::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
moderator::{ModLockPost, ModLockPostForm}, moderator::{ModLockPost, ModLockPostForm},
post::Post, post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -61,7 +61,11 @@ impl Perform for LockPost {
let post_id = data.post_id; let post_id = data.post_id;
let locked = data.locked; let locked = data.locked;
let updated_post: ApubPost = blocking(context.pool(), move |conn| { 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?? .await??
.into(); .into();

View file

@ -17,7 +17,7 @@ use lemmy_apub::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
moderator::{ModStickyPost, ModStickyPostForm}, moderator::{ModStickyPost, ModStickyPostForm},
post::Post, post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -61,7 +61,11 @@ impl Perform for StickyPost {
let post_id = data.post_id; let post_id = data.post_id;
let stickied = data.stickied; let stickied = data.stickied;
let updated_post: ApubPost = blocking(context.pool(), move |conn| { 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?? .await??
.into(); .into();

View file

@ -7,7 +7,10 @@ use lemmy_api_common::{
}; };
use lemmy_apub::protocol::activities::community::report::Report; use lemmy_apub::protocol::activities::community::report::Report;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::post_report::{PostReport, PostReportForm}, source::{
local_site::LocalSite,
post_report::{PostReport, PostReportForm},
},
traits::Reportable, traits::Reportable,
}; };
use lemmy_db_views::structs::{PostReportView, PostView}; use lemmy_db_views::structs::{PostReportView, PostView};
@ -28,9 +31,10 @@ impl Perform for CreatePostReport {
let data: &CreatePostReport = self; let data: &CreatePostReport = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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(); 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 person_id = local_user_view.person.id;
let post_id = data.post_id; let post_id = data.post_id;

View file

@ -4,7 +4,10 @@ use lemmy_api_common::{
private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse}, private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse},
utils::{blocking, get_local_user_view_from_jwt}, 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_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation}; 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 private_message_id = data.private_message_id;
let read = data.read; let read = data.read;
blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;

View file

@ -7,6 +7,7 @@ use lemmy_api_common::{
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::CommunityId, newtypes::CommunityId,
source::{ source::{
local_site::LocalSite,
private_message::PrivateMessage, private_message::PrivateMessage,
private_message_report::{PrivateMessageReport, PrivateMessageReportForm}, private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
}, },
@ -28,9 +29,10 @@ impl Perform for CreatePrivateMessageReport {
) -> Result<Self::Response, LemmyError> { ) -> Result<Self::Response, LemmyError> {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?; 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(); 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 person_id = local_user_view.person.id;
let private_message_id = self.private_message_id; let private_message_id = self.private_message_id;

View file

@ -2,14 +2,14 @@ use crate::Perform;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
site::{GetSiteResponse, LeaveAdmin}, 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::{ use lemmy_db_schema::{
source::{ source::{
actor_language::SiteLanguage, actor_language::SiteLanguage,
language::Language, language::Language,
moderator::{ModAdd, ModAddForm}, moderator::{ModAdd, ModAddForm},
person::Person, person::{Person, PersonUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -42,7 +42,11 @@ impl Perform for LeaveAdmin {
let person_id = local_user_view.person.id; let person_id = local_user_view.person.id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
Person::leave_admin(conn, person_id) Person::update(
conn,
person_id,
&PersonUpdateForm::builder().admin(Some(false)).build(),
)
}) })
.await??; .await??;
@ -59,18 +63,16 @@ impl Perform for LeaveAdmin {
let site_view = blocking(context.pool(), SiteView::read_local).await??; let site_view = blocking(context.pool(), SiteView::read_local).await??;
let admins = blocking(context.pool(), PersonViewSafe::admins).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 all_languages = blocking(context.pool(), Language::read_all).await??;
let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??; let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??;
Ok(GetSiteResponse { Ok(GetSiteResponse {
site_view: Some(site_view), site_view,
admins, admins,
online: 0, online: 0,
version: version::VERSION.to_string(), version: version::VERSION.to_string(),
my_user: None, my_user: None,
federated_instances, federated_instances: None,
all_languages, all_languages,
discussion_languages, discussion_languages,
}) })

View file

@ -12,7 +12,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{CommunityId, PersonId}, newtypes::{CommunityId, PersonId},
source::site::Site, source::local_site::LocalSite,
ModlogActionType, ModlogActionType,
}; };
use lemmy_db_views_moderator::structs::{ use lemmy_db_views_moderator::structs::{
@ -52,13 +52,13 @@ impl Perform for GetModlog {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 type_ = data.type_.unwrap_or(All);
let community_id = data.community_id; 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 { let (local_person_id, is_admin) = match local_user_view {
Some(s) => (s.person.id, is_admin(&s).is_ok()), Some(s) => (s.person.id, is_admin(&s).is_ok()),
None => (PersonId(-1), false), None => (PersonId(-1), false),
@ -71,7 +71,7 @@ impl Perform for GetModlog {
&& is_mod_or_admin(context.pool(), local_person_id, community_id_value) && is_mod_or_admin(context.pool(), local_person_id, community_id_value)
.await .await
.is_ok(); .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 { let mod_person_id = if hide_modlog_names {
None None

View file

@ -6,8 +6,8 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserUpdateForm},
registration_application::{RegistrationApplication, RegistrationApplicationForm}, registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm},
}, },
traits::Crud, traits::Crud,
utils::diesel_option_overwrite, utils::diesel_option_overwrite,
@ -36,10 +36,9 @@ impl Perform for ApproveRegistrationApplication {
// Update the registration with reason, admin_id // Update the registration with reason, admin_id
let deny_reason = diesel_option_overwrite(&data.deny_reason); let deny_reason = diesel_option_overwrite(&data.deny_reason);
let app_form = RegistrationApplicationForm { let app_form = RegistrationApplicationUpdateForm {
admin_id: Some(local_user_view.person.id), admin_id: Some(Some(local_user_view.person.id)),
deny_reason, deny_reason,
..RegistrationApplicationForm::default()
}; };
let registration_application = blocking(context.pool(), move |conn| { let registration_application = blocking(context.pool(), move |conn| {
@ -48,10 +47,9 @@ impl Perform for ApproveRegistrationApplication {
.await??; .await??;
// Update the local_user row // Update the local_user row
let local_user_form = LocalUserForm { let local_user_form = LocalUserUpdateForm::builder()
accepted_application: Some(data.approve), .accepted_application(Some(data.approve))
..LocalUserForm::default() .build();
};
let approved_user_id = registration_application.local_user_id; let approved_user_id = registration_application.local_user_id;
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {

View file

@ -4,7 +4,7 @@ use lemmy_api_common::{
site::{ListRegistrationApplications, ListRegistrationApplicationsResponse}, site::{ListRegistrationApplications, ListRegistrationApplicationsResponse},
utils::{blocking, get_local_user_view_from_jwt, is_admin}, 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_db_views::registration_application_view::RegistrationApplicationQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -22,14 +22,13 @@ impl Perform for ListRegistrationApplications {
let data = self; let data = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let unread_only = data.unread_only; let unread_only = data.unread_only;
let verified_email_only = blocking(context.pool(), Site::read_local) let verified_email_only = local_site.require_email_verification;
.await??
.require_email_verification;
let page = data.page; let page = data.page;
let limit = data.limit; let limit = data.limit;

View file

@ -4,7 +4,7 @@ use lemmy_api_common::{
site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse}, site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse},
utils::{blocking, get_local_user_view_from_jwt, is_admin}, 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_db_views::structs::RegistrationApplicationView;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -21,13 +21,12 @@ impl Perform for GetUnreadRegistrationApplicationCount {
let data = self; let data = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 // Only let admins do this
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
let verified_email_only = blocking(context.pool(), Site::read_local) let verified_email_only = local_site.require_email_verification;
.await??
.require_email_verification;
let registration_applications = blocking(context.pool(), move |conn| { let registration_applications = blocking(context.pool(), move |conn| {
RegistrationApplicationView::get_unread_count(conn, verified_email_only) RegistrationApplicationView::get_unread_count(conn, verified_email_only)

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, 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_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::structs::{CommentView, PostView};
use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe}; use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe};
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -25,7 +25,8 @@ impl Perform for ResolveObject {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret())
.await?; .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) let res = search_query_to_object_id(&self.q, local_user_view.is_none(), context)
.await .await

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
}; };
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::community::Community, source::{community::Community, local_site::LocalSite},
traits::DeleteableOrRemoveable, traits::DeleteableOrRemoveable,
utils::post_to_comment_sort_type, utils::post_to_comment_sort_type,
SearchType, SearchType,
@ -31,7 +31,9 @@ impl Perform for Search {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 person_id = local_user_view.as_ref().map(|u| u.person.id);
let local_user = local_user_view.map(|l| l.local_user); let local_user = local_user_view.map(|l| l.local_user);

View file

@ -35,6 +35,7 @@ percent-encoding = { version = "2.2.0", optional = true }
encoding = { version = "0.2.33", optional = true } encoding = { version = "0.2.33", optional = true }
reqwest-middleware = { version = "0.1.6", optional = true } reqwest-middleware = { version = "0.1.6", optional = true }
webpage = { version = "1.4.0", default-features = false, features = ["serde"], optional = true } webpage = { version = "1.4.0", default-features = false, features = ["serde"], optional = true }
regex = "1.6.0"
[dev-dependencies] [dev-dependencies]
actix-rt = { version = "2.7.0", default-features = false } actix-rt = { version = "2.7.0", default-features = false }

View file

@ -128,6 +128,30 @@ pub struct CreateSite {
pub application_email_admins: Option<bool>, pub application_email_admins: Option<bool>,
pub auth: Sensitive<String>, pub auth: Sensitive<String>,
pub hide_modlog_mod_names: Option<bool>, 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)] #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@ -151,6 +175,29 @@ pub struct EditSite {
pub application_email_admins: Option<bool>, pub application_email_admins: Option<bool>,
pub hide_modlog_mod_names: Option<bool>, pub hide_modlog_mod_names: Option<bool>,
pub discussion_languages: Option<Vec<LanguageId>>, 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>, pub auth: Sensitive<String>,
} }
@ -166,7 +213,7 @@ pub struct SiteResponse {
#[derive(Debug, Serialize, Deserialize, Clone)] #[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GetSiteResponse { 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 admins: Vec<PersonViewSafe>,
pub online: usize, pub online: usize,
pub version: String, pub version: String,

View file

@ -4,16 +4,18 @@ use lemmy_db_schema::{
impls::person::is_banned, impls::person::is_banned,
newtypes::{CommunityId, LocalUserId, PersonId, PostId}, newtypes::{CommunityId, LocalUserId, PersonId, PostId},
source::{ source::{
comment::Comment, comment::{Comment, CommentUpdateForm},
community::Community, community::{Community, CommunityUpdateForm},
email_verification::{EmailVerification, EmailVerificationForm}, email_verification::{EmailVerification, EmailVerificationForm},
instance::Instance,
local_site::LocalSite,
local_site_rate_limit::LocalSiteRateLimit,
password_reset_request::PasswordResetRequest, password_reset_request::PasswordResetRequest,
person::Person, person::{Person, PersonUpdateForm},
person_block::PersonBlock, person_block::PersonBlock,
post::{Post, PostRead, PostReadForm}, post::{Post, PostRead, PostReadForm},
registration_application::RegistrationApplication, registration_application::RegistrationApplication,
secret::Secret, secret::Secret,
site::Site,
}, },
traits::{Crud, Readable}, traits::{Crud, Readable},
utils::DbPool, utils::DbPool,
@ -32,9 +34,11 @@ use lemmy_utils::{
claims::Claims, claims::Claims,
email::{send_email, translations::Lang}, email::{send_email, translations::Lang},
error::LemmyError, error::LemmyError,
rate_limit::RateLimitConfig,
settings::structs::Settings, settings::structs::Settings,
utils::generate_random_string, utils::{build_slur_regex, generate_random_string},
}; };
use regex::Regex;
use reqwest_middleware::ClientWithMiddleware; use reqwest_middleware::ClientWithMiddleware;
use rosetta_i18n::{Language, LanguageId}; use rosetta_i18n::{Language, LanguageId};
use std::str::FromStr; use std::str::FromStr;
@ -265,67 +269,38 @@ pub async fn check_person_block(
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> { pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> {
if score == -1 { if score == -1 && !local_site.enable_downvotes {
let site = blocking(pool, Site::read_local).await??; return Err(LemmyError::from_message("downvotes_disabled"));
if !site.enable_downvotes {
return Err(LemmyError::from_message("downvotes_disabled"));
}
} }
Ok(()) Ok(())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn check_private_instance( pub fn check_private_instance(
local_user_view: &Option<LocalUserView>, local_user_view: &Option<LocalUserView>,
pool: &DbPool, local_site: &LocalSite,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if local_user_view.is_none() { if local_user_view.is_none() && local_site.private_instance {
let site = blocking(pool, Site::read_local).await?; return Err(LemmyError::from_message("instance_is_private"));
// The site might not be set up yet
if let Ok(site) = site {
if site.private_instance {
return Err(LemmyError::from_message("instance_is_private"));
}
}
} }
Ok(()) Ok(())
} }
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
pub async fn build_federated_instances( pub async fn build_federated_instances(
local_site: &LocalSite,
pool: &DbPool, pool: &DbPool,
settings: &Settings,
) -> Result<Option<FederatedInstances>, LemmyError> { ) -> Result<Option<FederatedInstances>, LemmyError> {
let federation_config = &settings.federation; if local_site.federation_enabled {
let hostname = &settings.hostname; // TODO I hate that this requires 3 queries
let federation = federation_config.to_owned(); let linked = blocking(pool, Instance::linked).await??;
if federation.enabled { let allowed = blocking(pool, Instance::allowlist).await??;
let distinct_communities = blocking(pool, move |conn| { let blocked = blocking(pool, Instance::blocklist).await??;
Community::distinct_federated_communities(conn)
})
.await??;
let allowed = federation.allowed_instances; // These can return empty vectors, so convert them to options
let blocked = federation.blocked_instances; let allowed = (!allowed.is_empty()).then(|| allowed);
let blocked = (!blocked.is_empty()).then(|| blocked);
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();
Ok(Some(FederatedInstances { Ok(Some(FederatedInstances {
linked, 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( pub fn send_application_approved_email(
user: &LocalUserView, user: &LocalUserView,
settings: &Settings, settings: &Settings,
@ -506,11 +512,11 @@ pub async fn send_new_applicant_email_to_admins(
} }
pub async fn check_registration_application( pub async fn check_registration_application(
site: &Site,
local_user_view: &LocalUserView, local_user_view: &LocalUserView,
local_site: &LocalSite,
pool: &DbPool, pool: &DbPool,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
if site.require_application if local_site.require_application
&& !local_user_view.local_user.accepted_application && !local_user_view.local_user.accepted_application
&& !local_user_view.person.admin && !local_user_view.person.admin
{ {
@ -531,19 +537,13 @@ pub async fn check_registration_application(
Ok(()) Ok(())
} }
/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done. pub fn check_private_instance_and_federation_enabled(
pub async fn check_private_instance_and_federation_enabled( local_site: &LocalSite,
pool: &DbPool,
settings: &Settings,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
let site_opt = blocking(pool, Site::read_local).await?; if local_site.private_instance && local_site.federation_enabled {
return Err(LemmyError::from_message(
if let Ok(site) = site_opt { "Cannot have both private instance and federation enabled.",
if site.private_instance && settings.federation.enabled { ));
return Err(LemmyError::from_message(
"Cannot have both private instance and federation enabled.",
));
}
} }
Ok(()) Ok(())
} }
@ -627,7 +627,14 @@ pub async fn remove_user_data(
// Update the fields to None // Update the fields to None
blocking(pool, move |conn| { 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??; .await??;
@ -656,8 +663,12 @@ pub async fn remove_user_data(
for first_mod_community in banned_user_first_communities { for first_mod_community in banned_user_first_communities {
let community_id = first_mod_community.community.id; let community_id = first_mod_community.community.id;
blocking(pool, move |conn: &mut _| { blocking(pool, move |conn| {
Community::update_removed(conn, community_id, true) Community::update(
conn,
community_id,
&CommunityUpdateForm::builder().removed(Some(true)).build(),
)
}) })
.await??; .await??;
@ -672,7 +683,14 @@ pub async fn remove_user_data(
} }
// Update the fields to None // Update the fields to None
blocking(pool, move |conn| { 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??; .await??;
} }
@ -713,7 +731,11 @@ pub async fn remove_user_data_in_community(
for comment_view in &comments { for comment_view in &comments {
let comment_id = comment_view.comment.id; let comment_id = comment_view.comment.id;
blocking(pool, move |conn| { blocking(pool, move |conn| {
Comment::update_removed(conn, comment_id, true) Comment::update(
conn,
comment_id,
&CommentUpdateForm::builder().removed(Some(true)).build(),
)
}) })
.await??; .await??;
} }
@ -761,15 +783,11 @@ pub async fn delete_user_account(
Ok(()) Ok(())
} }
pub async fn listing_type_with_site_default( pub fn listing_type_with_site_default(
listing_type: Option<ListingType>, listing_type: Option<ListingType>,
pool: &DbPool, local_site: &LocalSite,
) -> Result<ListingType, LemmyError> { ) -> Result<ListingType, LemmyError> {
Ok(match listing_type { Ok(listing_type.unwrap_or(ListingType::from_str(
Some(l) => l, &local_site.default_post_listing_type,
None => { )?))
let site = blocking(pool, Site::read_local).await??;
ListingType::from_str(&site.default_post_listing_type)?
}
})
} }

View file

@ -9,6 +9,7 @@ use lemmy_api_common::{
check_post_deleted_or_removed, check_post_deleted_or_removed,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
get_post, get_post,
local_site_to_slur_regex,
}, },
}; };
use lemmy_apub::{ use lemmy_apub::{
@ -20,9 +21,10 @@ use lemmy_apub::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm},
comment_reply::CommentReply, comment_reply::{CommentReply, CommentReplyUpdateForm},
person_mention::PersonMention, local_site::LocalSite,
person_mention::{PersonMention, PersonMentionUpdateForm},
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
}; };
@ -50,9 +52,12 @@ impl PerformCrud for CreateComment {
let data: &CreateComment = self; let data: &CreateComment = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 = let content_slurs_removed = remove_slurs(
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); &data.content.to_owned(),
&local_site_to_slur_regex(&local_site),
);
// Check for a community ban // Check for a community ban
let post_id = data.post_id; let post_id = data.post_id;
@ -97,13 +102,12 @@ impl PerformCrud for CreateComment {
}) })
.await??; .await??;
let comment_form = CommentForm { let comment_form = CommentInsertForm::builder()
content: content_slurs_removed, .content(content_slurs_removed.to_owned())
post_id: data.post_id, .post_id(data.post_id)
creator_id: local_user_view.person.id, .creator_id(local_user_view.person.id)
language_id: Some(language_id), .language_id(Some(language_id))
..CommentForm::default() .build();
};
// Create the comment // Create the comment
let comment_form2 = comment_form.clone(); let comment_form2 = comment_form.clone();
@ -125,14 +129,18 @@ impl PerformCrud for CreateComment {
&inserted_comment_id.to_string(), &inserted_comment_id.to_string(),
&protocol_and_hostname, &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? .await?
.map_err(|e| e.with_message("couldnt_create_comment"))?; .map_err(|e| e.with_message("couldnt_create_comment"))?;
// Scan the comment for user mentions, add those rows // Scan the comment for user mentions, add those rows
let post_id = post.id; 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( let recipient_ids = send_local_notifs(
mentions, mentions,
&updated_comment, &updated_comment,
@ -175,7 +183,7 @@ impl PerformCrud for CreateComment {
.await?; .await?;
if let Ok(reply) = comment_reply { if let Ok(reply) = comment_reply {
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
CommentReply::update_read(conn, reply.id, true) CommentReply::update(conn, reply.id, &CommentReplyUpdateForm { read: Some(true) })
}) })
.await? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_replies"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_replies"))?;
@ -189,7 +197,11 @@ impl PerformCrud for CreateComment {
.await?; .await?;
if let Ok(mention) = person_mention { if let Ok(mention) = person_mention {
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
PersonMention::update_read(conn, mention.id, true) PersonMention::update(
conn,
mention.id,
&PersonMentionUpdateForm { read: Some(true) },
)
}) })
.await? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_person_mentions"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_person_mentions"))?;

View file

@ -6,7 +6,11 @@ use lemmy_api_common::{
}; };
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{comment::Comment, community::Community, post::Post}, source::{
comment::{Comment, CommentUpdateForm},
community::Community,
post::Post,
},
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::CommentView; use lemmy_db_views::structs::CommentView;
@ -57,7 +61,11 @@ impl PerformCrud for DeleteComment {
// Do the delete // Do the delete
let deleted = data.deleted; let deleted = data.deleted;
let updated_comment = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;

View file

@ -11,7 +11,7 @@ use lemmy_api_common::{
}; };
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{comment::Comment, community::Community}, source::{comment::Comment, community::Community, local_site::LocalSite},
traits::{Crud, DeleteableOrRemoveable}, traits::{Crud, DeleteableOrRemoveable},
}; };
use lemmy_db_views::comment_view::CommentQuery; use lemmy_db_views::comment_view::CommentQuery;
@ -32,10 +32,11 @@ impl PerformCrud for GetComments {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 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 { let community_actor_id = if let Some(name) = &data.community_name {
resolve_actor_identifier::<ApubCommunity, Community>(name, context, true) resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)

View file

@ -4,6 +4,7 @@ use lemmy_api_common::{
comment::{CommentResponse, GetComment}, comment::{CommentResponse, GetComment},
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, 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_db_views::structs::CommentView;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -22,8 +23,9 @@ impl PerformCrud for GetComment {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 person_id = local_user_view.map(|u| u.person.id);
let id = data.id; let id = data.id;

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::{Comment, CommentUpdateForm},
community::Community, community::Community,
moderator::{ModRemoveComment, ModRemoveCommentForm}, moderator::{ModRemoveComment, ModRemoveCommentForm},
post::Post, post::Post,
@ -60,7 +60,11 @@ impl PerformCrud for RemoveComment {
// Do the remove // Do the remove
let removed = data.removed; let removed = data.removed;
let updated_comment = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?;

View file

@ -8,6 +8,7 @@ use lemmy_api_common::{
check_post_deleted_or_removed, check_post_deleted_or_removed,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
is_mod_or_admin, is_mod_or_admin,
local_site_to_slur_regex,
}, },
}; };
use lemmy_apub::protocol::activities::{ use lemmy_apub::protocol::activities::{
@ -17,7 +18,8 @@ use lemmy_apub::protocol::activities::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
comment::{Comment, CommentForm}, comment::{Comment, CommentUpdateForm},
local_site::LocalSite,
}, },
traits::Crud, traits::Crud,
}; };
@ -48,6 +50,7 @@ impl PerformCrud for EditComment {
let data: &EditComment = self; let data: &EditComment = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 comment_id = data.comment_id;
let orig_comment = blocking(context.pool(), move |conn| { let orig_comment = blocking(context.pool(), move |conn| {
@ -90,16 +93,13 @@ impl PerformCrud for EditComment {
let content_slurs_removed = data let content_slurs_removed = data
.content .content
.as_ref() .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 comment_id = data.comment_id;
let form = CommentForm { let form = CommentUpdateForm::builder()
creator_id: orig_comment.comment.creator_id, .content(content_slurs_removed)
post_id: orig_comment.comment.post_id, .distinguished(data.distinguished)
content: content_slurs_removed.unwrap_or(orig_comment.comment.content), .language_id(data.language_id)
distinguished: data.distinguished, .build();
language_id: data.language_id,
..Default::default()
};
let updated_comment = blocking(context.pool(), move |conn| { let updated_comment = blocking(context.pool(), move |conn| {
Comment::update(conn, comment_id, &form) Comment::update(conn, comment_id, &form)
}) })

View file

@ -3,7 +3,7 @@ use activitypub_federation::core::{object_id::ObjectId, signatures::generate_act
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
community::{CommunityResponse, CreateCommunity}, 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::{ use lemmy_apub::{
generate_followers_url, generate_followers_url,
@ -14,20 +14,18 @@ use lemmy_apub::{
EndpointType, EndpointType,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::community::{
community::{ Community,
Community, CommunityFollower,
CommunityFollower, CommunityFollowerForm,
CommunityFollowerForm, CommunityInsertForm,
CommunityForm, CommunityModerator,
CommunityModerator, CommunityModeratorForm,
CommunityModeratorForm,
},
site::Site,
}, },
traits::{Crud, Followable, Joinable}, 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_db_views_actor::structs::CommunityView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::LemmyError,
@ -49,8 +47,9 @@ impl PerformCrud for CreateCommunity {
let data: &CreateCommunity = self; let data: &CreateCommunity = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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() { if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() {
return Err(LemmyError::from_message( return Err(LemmyError::from_message(
"only_admins_can_create_communities", "only_admins_can_create_communities",
@ -58,15 +57,15 @@ impl PerformCrud for CreateCommunity {
} }
// Check to make sure the icon and banners are urls // Check to make sure the icon and banners are urls
let icon = diesel_option_overwrite_to_url(&data.icon)?; let icon = diesel_option_overwrite_to_url_create(&data.icon)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url_create(&data.banner)?;
let description = diesel_option_overwrite(&data.description);
check_slurs(&data.name, &context.settings().slur_regex())?; let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs(&data.title, &context.settings().slur_regex())?; check_slurs(&data.name, &slur_regex)?;
check_slurs_opt(&data.description, &context.settings().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")); 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 // When you create a community, make sure the user becomes a moderator and a follower
let keypair = generate_actor_keypair()?; let keypair = generate_actor_keypair()?;
let community_form = CommunityForm { let community_form = CommunityInsertForm::builder()
name: data.name.to_owned(), .name(data.name.to_owned())
title: data.title.to_owned(), .title(data.title.to_owned())
description, .description(data.description.to_owned())
icon, .icon(icon)
banner, .banner(banner)
nsfw: data.nsfw, .nsfw(data.nsfw)
actor_id: Some(community_actor_id.to_owned()), .actor_id(Some(community_actor_id.to_owned()))
private_key: Some(Some(keypair.private_key)), .private_key(Some(keypair.private_key))
public_key: Some(keypair.public_key), .public_key(keypair.public_key)
followers_url: Some(generate_followers_url(&community_actor_id)?), .followers_url(Some(generate_followers_url(&community_actor_id)?))
inbox_url: Some(generate_inbox_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)?)), .shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?))
posting_restricted_to_mods: data.posting_restricted_to_mods, .posting_restricted_to_mods(data.posting_restricted_to_mods)
..CommunityForm::default() .instance_id(site_view.site.instance_id)
}; .build();
let inserted_community = blocking(context.pool(), move |conn| { let inserted_community = blocking(context.pool(), move |conn| {
Community::create(conn, &community_form) Community::create(conn, &community_form)

View file

@ -5,7 +5,10 @@ use lemmy_api_common::{
utils::{blocking, get_local_user_view_from_jwt}, utils::{blocking, get_local_user_view_from_jwt},
}; };
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; 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_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; 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 community_id = data.community_id;
let deleted = data.deleted; let deleted = data.deleted;
let updated_community = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;

View file

@ -4,7 +4,7 @@ use lemmy_api_common::{
community::{ListCommunities, ListCommunitiesResponse}, community::{ListCommunities, ListCommunitiesResponse},
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, 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_db_views_actor::community_view::CommunityQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -23,8 +23,9 @@ impl PerformCrud for ListCommunities {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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); let person_id = local_user_view.to_owned().map(|l| l.person.id);

View file

@ -10,7 +10,12 @@ use lemmy_apub::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
impls::actor_language::default_post_language, 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, traits::DeleteableOrRemoveable,
}; };
use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView};
@ -31,12 +36,13 @@ impl PerformCrud for GetCommunity {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .await?;
let local_site = blocking(context.pool(), LocalSite::read).await??;
if data.name.is_none() && data.id.is_none() { if data.name.is_none() && data.id.is_none() {
return Err(LemmyError::from_message("no_id_given")); 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); let person_id = local_user_view.as_ref().map(|u| u.person.id);

View file

@ -7,7 +7,7 @@ use lemmy_api_common::{
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::Community, community::{Community, CommunityUpdateForm},
moderator::{ModRemoveCommunity, ModRemoveCommunityForm}, moderator::{ModRemoveCommunity, ModRemoveCommunityForm},
}, },
traits::Crud, traits::Crud,
@ -36,7 +36,13 @@ impl PerformCrud for RemoveCommunity {
let community_id = data.community_id; let community_id = data.community_id;
let removed = data.removed; let removed = data.removed;
let updated_community = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?;

View file

@ -2,17 +2,18 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
community::{CommunityResponse, EditCommunity}, 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_apub::protocol::activities::community::update::UpdateCommunity;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::{LanguageId, PersonId}, newtypes::{LanguageId, PersonId},
source::{ source::{
actor_language::{CommunityLanguage, SiteLanguage}, actor_language::{CommunityLanguage, SiteLanguage},
community::{Community, CommunityForm}, community::{Community, CommunityUpdateForm},
local_site::LocalSite,
}, },
traits::Crud, 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_db_views_actor::structs::CommunityModeratorView;
use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId}; use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId};
@ -31,13 +32,15 @@ impl PerformCrud for EditCommunity {
let data: &EditCommunity = self; let data: &EditCommunity = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url(&data.banner)?;
let description = diesel_option_overwrite(&data.description); let description = diesel_option_overwrite(&data.description);
check_slurs_opt(&data.title, &context.settings().slur_regex())?; let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs_opt(&data.title, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?;
// Verify its a mod (only mods can edit it) // Verify its a mod (only mods can edit it)
let community_id = data.community_id; let community_id = data.community_id;
@ -66,22 +69,14 @@ impl PerformCrud for EditCommunity {
.await??; .await??;
} }
let read_community = blocking(context.pool(), move |conn| { let community_form = CommunityUpdateForm::builder()
Community::read(conn, community_id) .title(data.title.to_owned())
}) .description(description)
.await??; .icon(icon)
.banner(banner)
let community_form = CommunityForm { .nsfw(data.nsfw)
name: read_community.name, .posting_restricted_to_mods(data.posting_restricted_to_mods)
title: data.title.to_owned().unwrap_or(read_community.title), .build();
description,
icon,
banner,
nsfw: data.nsfw,
posting_restricted_to_mods: data.posting_restricted_to_mods,
updated: Some(naive_now()),
..CommunityForm::default()
};
let community_id = data.community_id; let community_id = data.community_id;
let updated_community = blocking(context.pool(), move |conn| { let updated_community = blocking(context.pool(), move |conn| {

View file

@ -9,6 +9,7 @@ use lemmy_api_common::{
check_community_deleted_or_removed, check_community_deleted_or_removed,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
honeypot_check, honeypot_check,
local_site_to_slur_regex,
mark_post_as_read, mark_post_as_read,
}, },
}; };
@ -23,10 +24,10 @@ use lemmy_db_schema::{
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
community::Community, community::Community,
post::{Post, PostForm, PostLike, PostLikeForm}, local_site::LocalSite,
post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm},
}, },
traits::{Crud, Likeable}, traits::{Crud, Likeable},
utils::diesel_option_overwrite,
}; };
use lemmy_db_views_actor::structs::CommunityView; use lemmy_db_views_actor::structs::CommunityView;
use lemmy_utils::{ use lemmy_utils::{
@ -52,15 +53,15 @@ impl PerformCrud for CreatePost {
let data: &CreatePost = self; let data: &CreatePost = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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(); let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs(&data.name, slur_regex)?; check_slurs(&data.name, &slur_regex)?;
check_slurs_opt(&data.body, slur_regex)?; check_slurs_opt(&data.body, &slur_regex)?;
honeypot_check(&data.honeypot)?; honeypot_check(&data.honeypot)?;
let data_url = data.url.as_ref(); 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 url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear"
let body = diesel_option_overwrite(&data.body);
if !is_valid_post_title(&data.name) { if !is_valid_post_title(&data.name) {
return Err(LemmyError::from_message("invalid_post_title")); return Err(LemmyError::from_message("invalid_post_title"));
@ -89,7 +90,7 @@ impl PerformCrud for CreatePost {
let (metadata_res, thumbnail_url) = let (metadata_res, thumbnail_url) =
fetch_site_data(context.client(), context.settings(), data_url).await; fetch_site_data(context.client(), context.settings(), data_url).await;
let (embed_title, embed_description, embed_video_url) = metadata_res 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(); .unwrap_or_default();
let language_id = match data.language_id { let language_id = match data.language_id {
@ -106,20 +107,19 @@ impl PerformCrud for CreatePost {
}) })
.await??; .await??;
let post_form = PostForm { let post_form = PostInsertForm::builder()
name: data.name.trim().to_owned(), .name(data.name.trim().to_owned())
url, .url(url)
body, .body(data.body.to_owned())
community_id: data.community_id, .community_id(data.community_id)
creator_id: local_user_view.person.id, .creator_id(local_user_view.person.id)
nsfw: data.nsfw, .nsfw(data.nsfw)
embed_title, .embed_title(embed_title)
embed_description, .embed_description(embed_description)
embed_video_url, .embed_video_url(embed_video_url)
language_id, .language_id(language_id)
thumbnail_url: Some(thumbnail_url), .thumbnail_url(thumbnail_url)
..PostForm::default() .build();
};
let inserted_post = let inserted_post =
match blocking(context.pool(), move |conn| Post::create(conn, &post_form)).await? { 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(), &inserted_post_id.to_string(),
&protocol_and_hostname, &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? .await?
.map_err(|e| e.with_message("couldnt_create_post"))?; .map_err(|e| e.with_message("couldnt_create_post"))?;

View file

@ -11,7 +11,10 @@ use lemmy_api_common::{
}; };
use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{community::Community, post::Post}, source::{
community::Community,
post::{Post, PostUpdateForm},
},
traits::Crud, traits::Crud,
}; };
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -56,7 +59,11 @@ impl PerformCrud for DeletePost {
let post_id = data.post_id; let post_id = data.post_id;
let deleted = data.deleted; let deleted = data.deleted;
let updated_post = blocking(context.pool(), move |conn| { 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??; .await??;

View file

@ -10,7 +10,10 @@ use lemmy_api_common::{
}, },
}; };
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; 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_db_views::post_view::PostQuery;
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -29,13 +32,14 @@ impl PerformCrud for GetPosts {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 is_logged_in = local_user_view.is_some();
let sort = data.sort; 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 page = data.page;
let limit = data.limit; let limit = data.limit;

View file

@ -6,7 +6,7 @@ use lemmy_api_common::{
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm},
source::comment::Comment, source::{comment::Comment, local_site::LocalSite},
traits::{Crud, DeleteableOrRemoveable}, traits::{Crud, DeleteableOrRemoveable},
}; };
use lemmy_db_views::structs::PostView; use lemmy_db_views::structs::PostView;
@ -28,8 +28,9 @@ impl PerformCrud for GetPost {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 person_id = local_user_view.map(|u| u.person.id);

View file

@ -9,7 +9,7 @@ use lemmy_db_schema::{
source::{ source::{
community::Community, community::Community,
moderator::{ModRemovePost, ModRemovePostForm}, moderator::{ModRemovePost, ModRemovePostForm},
post::Post, post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -52,7 +52,11 @@ impl PerformCrud for RemovePost {
let post_id = data.post_id; let post_id = data.post_id;
let removed = data.removed; let removed = data.removed;
let updated_post = blocking(context.pool(), move |conn| { 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??; .await??;

View file

@ -8,6 +8,7 @@ use lemmy_api_common::{
check_community_ban, check_community_ban,
check_community_deleted_or_removed, check_community_deleted_or_removed,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
local_site_to_slur_regex,
}, },
}; };
use lemmy_apub::protocol::activities::{ use lemmy_apub::protocol::activities::{
@ -17,10 +18,11 @@ use lemmy_apub::protocol::activities::{
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
actor_language::CommunityLanguage, actor_language::CommunityLanguage,
post::{Post, PostForm}, local_site::LocalSite,
post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
utils::{diesel_option_overwrite, naive_now}, utils::diesel_option_overwrite,
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::LemmyError,
@ -42,6 +44,7 @@ impl PerformCrud for EditPost {
let data: &EditPost = self; let data: &EditPost = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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(); 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 url = Some(data_url.map(clean_url_params).map(Into::into));
let body = diesel_option_overwrite(&data.body); let body = diesel_option_overwrite(&data.body);
let slur_regex = &context.settings().slur_regex(); let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.name, slur_regex)?; check_slurs_opt(&data.name, &slur_regex)?;
check_slurs_opt(&data.body, slur_regex)?; check_slurs_opt(&data.body, &slur_regex)?;
if let Some(name) = &data.name { if let Some(name) = &data.name {
if !is_valid_post_title(name) { if !is_valid_post_title(name) {
@ -90,21 +93,17 @@ impl PerformCrud for EditPost {
}) })
.await??; .await??;
let post_form = PostForm { let post_form = PostUpdateForm::builder()
creator_id: orig_post.creator_id.to_owned(), .name(data.name.to_owned())
community_id: orig_post.community_id, .url(url)
name: data.name.to_owned().unwrap_or(orig_post.name), .body(body)
url, .nsfw(data.nsfw)
body, .embed_title(embed_title)
nsfw: data.nsfw, .embed_description(embed_description)
updated: Some(naive_now()), .embed_video_url(embed_video_url)
embed_title, .language_id(data.language_id)
embed_description, .thumbnail_url(Some(thumbnail_url))
embed_video_url, .build();
language_id: data.language_id,
thumbnail_url: Some(thumbnail_url),
..PostForm::default()
};
let post_id = data.post_id; let post_id = data.post_id;
let res = blocking(context.pool(), move |conn| { let res = blocking(context.pool(), move |conn| {

View file

@ -7,6 +7,7 @@ use lemmy_api_common::{
check_person_block, check_person_block,
get_interface_language, get_interface_language,
get_local_user_view_from_jwt, get_local_user_view_from_jwt,
local_site_to_slur_regex,
send_email_to_user, send_email_to_user,
}, },
}; };
@ -19,7 +20,10 @@ use lemmy_apub::{
EndpointType, EndpointType,
}; };
use lemmy_db_schema::{ use lemmy_db_schema::{
source::private_message::{PrivateMessage, PrivateMessageForm}, source::{
local_site::LocalSite,
private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm},
},
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::LocalUserView;
@ -39,18 +43,20 @@ impl PerformCrud for CreatePrivateMessage {
let data: &CreatePrivateMessage = self; let data: &CreatePrivateMessage = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 = let content_slurs_removed = remove_slurs(
remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); &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?; check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?;
let private_message_form = PrivateMessageForm { let private_message_form = PrivateMessageInsertForm::builder()
content: content_slurs_removed.to_owned(), .content(content_slurs_removed.to_owned())
creator_id: local_user_view.person.id, .creator_id(local_user_view.person.id)
recipient_id: data.recipient_id, .recipient_id(data.recipient_id)
..PrivateMessageForm::default() .build();
};
let inserted_private_message = match blocking(context.pool(), move |conn| { let inserted_private_message = match blocking(context.pool(), move |conn| {
PrivateMessage::create(conn, &private_message_form) PrivateMessage::create(conn, &private_message_form)
@ -76,10 +82,12 @@ impl PerformCrud for CreatePrivateMessage {
&inserted_private_message_id.to_string(), &inserted_private_message_id.to_string(),
&protocol_and_hostname, &protocol_and_hostname,
)?; )?;
Ok(PrivateMessage::update_ap_id( Ok(PrivateMessage::update(
conn, conn,
inserted_private_message_id, inserted_private_message.id,
apub_id, &PrivateMessageUpdateForm::builder()
.ap_id(Some(apub_id))
.build(),
)?) )?)
}, },
) )

View file

@ -5,7 +5,10 @@ use lemmy_api_common::{
utils::{blocking, get_local_user_view_from_jwt}, utils::{blocking, get_local_user_view_from_jwt},
}; };
use lemmy_apub::activities::deletion::send_apub_delete_private_message; 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_utils::{error::LemmyError, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; 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 private_message_id = data.private_message_id;
let deleted = data.deleted; let deleted = data.deleted;
let updated_private_message = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;

View file

@ -2,13 +2,20 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
private_message::{EditPrivateMessage, PrivateMessageResponse}, 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::{ use lemmy_apub::protocol::activities::{
create_or_update::private_message::CreateOrUpdatePrivateMessage, create_or_update::private_message::CreateOrUpdatePrivateMessage,
CreateOrUpdateType, 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_utils::{error::LemmyError, utils::remove_slurs, ConnectionId};
use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud};
@ -25,6 +32,7 @@ impl PerformCrud for EditPrivateMessage {
let data: &EditPrivateMessage = self; let data: &EditPrivateMessage = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
let local_site = blocking(context.pool(), LocalSite::read).await??;
// Checking permissions // Checking permissions
let private_message_id = data.private_message_id; let private_message_id = data.private_message_id;
@ -37,10 +45,17 @@ impl PerformCrud for EditPrivateMessage {
} }
// Doing the update // 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 private_message_id = data.private_message_id;
let updated_private_message = blocking(context.pool(), move |conn| { 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? .await?
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?;

View file

@ -3,19 +3,29 @@ use activitypub_federation::core::signatures::generate_actor_keypair;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
site::{CreateSite, SiteResponse}, 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_apub::generate_site_inbox_url;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::DbUrl, newtypes::DbUrl,
source::site::{Site, SiteForm}, source::{
local_site::{LocalSite, LocalSiteUpdateForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
site::{Site, SiteUpdateForm},
},
traits::Crud, traits::Crud,
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
}; };
use lemmy_db_views::structs::SiteView; use lemmy_db_views::structs::SiteView;
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::LemmyError,
utils::{check_slurs, check_slurs_opt}, utils::{check_application_question, check_slurs, check_slurs_opt},
ConnectionId, ConnectionId,
}; };
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -33,8 +43,9 @@ impl PerformCrud for CreateSite {
) -> Result<SiteResponse, LemmyError> { ) -> Result<SiteResponse, LemmyError> {
let data: &CreateSite = self; let data: &CreateSite = self;
let read_site = Site::read_local; let local_site = blocking(context.pool(), LocalSite::read).await??;
if blocking(context.pool(), read_site).await?.is_ok() {
if local_site.site_setup {
return Err(LemmyError::from_message("site_already_exists")); 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 icon = diesel_option_overwrite_to_url(&data.icon)?;
let banner = diesel_option_overwrite_to_url(&data.banner)?; let banner = diesel_option_overwrite_to_url(&data.banner)?;
check_slurs(&data.name, &context.settings().slur_regex())?; let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs_opt(&data.description, &context.settings().slur_regex())?; check_slurs(&data.name, &slur_regex)?;
check_slurs_opt(&data.description, &slur_regex)?;
// Make sure user is an admin // Make sure user is an admin
is_admin(&local_user_view)?; is_admin(&local_user_view)?;
@ -56,35 +68,82 @@ impl PerformCrud for CreateSite {
site_description_length_check(desc)?; 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 actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
let inbox_url = Some(generate_site_inbox_url(&actor_id)?); let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
let keypair = generate_actor_keypair()?; let keypair = generate_actor_keypair()?;
let site_form = SiteForm { let site_form = SiteUpdateForm::builder()
name: data.name.to_owned(), .name(Some(data.name.to_owned()))
sidebar, .sidebar(sidebar)
description, .description(description)
icon, .icon(icon)
banner, .banner(banner)
enable_downvotes: data.enable_downvotes, .actor_id(Some(actor_id))
open_registration: data.open_registration, .last_refreshed_at(Some(naive_now()))
enable_nsfw: data.enable_nsfw, .inbox_url(inbox_url)
community_creation_admin_only: data.community_creation_admin_only, .private_key(Some(Some(keypair.private_key)))
actor_id: Some(actor_id), .public_key(Some(keypair.public_key))
last_refreshed_at: Some(naive_now()), .build();
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 create_site = move |conn: &mut _| Site::create(conn, &site_form); let site_id = local_site.site_id;
blocking(context.pool(), create_site) blocking(context.pool(), move |conn| {
.await? Site::update(conn, site_id, &site_form)
.map_err(|e| LemmyError::from_error_message(e, "site_already_exists"))?; })
.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??; let site_view = blocking(context.pool(), SiteView::read_local).await??;

View file

@ -1,8 +1,7 @@
use crate::PerformCrud; use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
person::Register, site::{GetSite, GetSiteResponse, MyUserInfo},
site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo},
utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt}, utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt},
}; };
use lemmy_db_schema::source::{actor_language::SiteLanguage, language::Language}; 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_utils::{error::LemmyError, version, ConnectionId};
use lemmy_websocket::{messages::GetUsersOnline, LemmyContext}; use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
use tracing::info;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for GetSite { impl PerformCrud for GetSite {
type Response = GetSiteResponse; type Response = GetSiteResponse;
#[tracing::instrument(skip(context, websocket_id))] #[tracing::instrument(skip(context, _websocket_id))]
async fn perform( async fn perform(
&self, &self,
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
websocket_id: Option<ConnectionId>, _websocket_id: Option<ConnectionId>,
) -> Result<GetSiteResponse, LemmyError> { ) -> Result<GetSiteResponse, LemmyError> {
let data: &GetSite = self; let data: &GetSite = self;
let site_view = match blocking(context.pool(), SiteView::read_local).await? { let site_view = 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 admins = blocking(context.pool(), PersonViewSafe::admins).await??; let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
@ -130,7 +93,8 @@ impl PerformCrud for GetSite {
None 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 all_languages = blocking(context.pool(), Language::read_all).await??;
let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??; let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??;

View file

@ -2,22 +2,36 @@ use crate::PerformCrud;
use actix_web::web::Data; use actix_web::web::Data;
use lemmy_api_common::{ use lemmy_api_common::{
site::{EditSite, SiteResponse}, 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::{ use lemmy_db_schema::{
source::{ source::{
actor_language::SiteLanguage, actor_language::SiteLanguage,
federation_allowlist::FederationAllowList,
federation_blocklist::FederationBlockList,
local_site::{LocalSite, LocalSiteUpdateForm},
local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
local_user::LocalUser, local_user::LocalUser,
site::{Site, SiteForm}, site::{Site, SiteUpdateForm},
}, },
traits::Crud, traits::Crud,
utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
ListingType, ListingType,
}; };
use lemmy_db_views::structs::SiteView; 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 lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud};
use std::{default::Default, str::FromStr}; use std::str::FromStr;
#[async_trait::async_trait(?Send)] #[async_trait::async_trait(?Send)]
impl PerformCrud for EditSite { impl PerformCrud for EditSite {
@ -32,32 +46,22 @@ impl PerformCrud for EditSite {
let data: &EditSite = self; let data: &EditSite = self;
let local_user_view = let local_user_view =
get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; 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 // Make sure user is an admin
is_admin(&local_user_view)?; 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); check_slurs_opt(&data.name, &slur_regex)?;
let description = diesel_option_overwrite(&data.description); check_slurs_opt(&data.description, &slur_regex)?;
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, &context.settings().slur_regex())?; if let Some(desc) = &data.description {
check_slurs_opt(&data.description, &context.settings().slur_regex())?;
if let Some(Some(desc)) = &description {
site_description_length_check(desc)?; site_description_length_check(desc)?;
} }
// Make sure if applications are required, that there is an application questionnaire let application_question = diesel_option_overwrite(&data.application_question);
if data.require_application.unwrap_or(false) check_application_question(&application_question, &data.require_application)?;
&& application_question.as_ref().unwrap_or(&None).is_none()
{
return Err(LemmyError::from_message("application_question_required"));
}
if let Some(default_post_listing_type) = &data.default_post_listing_type { if let Some(default_post_listing_type) = &data.default_post_listing_type {
// only allow all or local as default listing types // 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() { if let Some(discussion_languages) = data.discussion_languages.clone() {
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
SiteLanguage::update(conn, discussion_languages.clone(), site_id) SiteLanguage::update(conn, discussion_languages.clone(), site_id)
@ -77,41 +81,104 @@ impl PerformCrud for EditSite {
.await??; .await??;
} }
let site_form = SiteForm { let name = data.name.to_owned();
name: data.name.to_owned().unwrap_or(local_site.name), let site_form = SiteUpdateForm::builder()
sidebar, .name(name)
description, .sidebar(diesel_option_overwrite(&data.sidebar))
icon, .description(diesel_option_overwrite(&data.description))
banner, .icon(diesel_option_overwrite_to_url(&data.icon)?)
updated: Some(naive_now()), .banner(diesel_option_overwrite_to_url(&data.banner)?)
enable_downvotes: data.enable_downvotes, .updated(Some(Some(naive_now())))
open_registration: data.open_registration, .build();
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 update_site = blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
Site::update(conn, local_site.id, &site_form) Site::update(conn, site_id, &site_form)
}) })
.await? .await
.map_err(|e| LemmyError::from_error_message(e, "couldnt_update_site"))?; // 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. // TODO can't think of a better way to do this.
// If the server suddenly requires email verification, or required applications, no old users // 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. // 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. // 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| { blocking(context.pool(), move |conn| {
LocalUser::set_all_users_registration_applications_accepted(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"))?; .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| { blocking(context.pool(), move |conn| {
LocalUser::set_all_users_email_verified(conn) LocalUser::set_all_users_email_verified(conn)
}) })

View file

@ -6,6 +6,7 @@ use lemmy_api_common::{
utils::{ utils::{
blocking, blocking,
honeypot_check, honeypot_check,
local_site_to_slur_regex,
password_length_check, password_length_check,
send_new_applicant_email_to_admins, send_new_applicant_email_to_admins,
send_verification_email, send_verification_email,
@ -20,14 +21,13 @@ use lemmy_apub::{
use lemmy_db_schema::{ use lemmy_db_schema::{
aggregates::structs::PersonAggregates, aggregates::structs::PersonAggregates,
source::{ source::{
local_user::{LocalUser, LocalUserForm}, local_user::{LocalUser, LocalUserInsertForm},
person::{Person, PersonForm}, person::{Person, PersonInsertForm},
registration_application::{RegistrationApplication, RegistrationApplicationForm}, registration_application::{RegistrationApplication, RegistrationApplicationInsertForm},
site::Site,
}, },
traits::Crud, traits::Crud,
}; };
use lemmy_db_views::structs::LocalUserView; use lemmy_db_views::structs::{LocalUserView, SiteView};
use lemmy_db_views_actor::structs::PersonViewSafe; use lemmy_db_views_actor::structs::PersonViewSafe;
use lemmy_utils::{ use lemmy_utils::{
claims::Claims, claims::Claims,
@ -49,27 +49,21 @@ impl PerformCrud for Register {
) -> Result<LoginResponse, LemmyError> { ) -> Result<LoginResponse, LemmyError> {
let data: &Register = self; let data: &Register = self;
// no email verification, or applications if the site is not setup yet let site_view = blocking(context.pool(), SiteView::read_local).await??;
let (mut email_verification, mut require_application) = (false, false); let local_site = site_view.local_site;
// Make sure site has open registration if !local_site.open_registration {
let site = blocking(context.pool(), Site::read_local).await?; return Err(LemmyError::from_message("registration_closed"));
if let Ok(site) = &site {
if !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)?; password_length_check(&data.password)?;
honeypot_check(&data.honeypot)?; 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")); 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( return Err(LemmyError::from_message(
"registration_application_answer_required", "registration_application_answer_required",
)); ));
@ -87,7 +81,7 @@ impl PerformCrud for Register {
.await??; .await??;
// If its not the admin, check the captcha // 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 let check = context
.chat_server() .chat_server()
.send(CheckCaptcha { .send(CheckCaptcha {
@ -106,12 +100,12 @@ impl PerformCrud for Register {
} }
} }
let slur_regex = &context.settings().slur_regex(); let slur_regex = local_site_to_slur_regex(&local_site);
check_slurs(&data.username, slur_regex)?; check_slurs(&data.username, &slur_regex)?;
check_slurs_opt(&data.answer, slur_regex)?; check_slurs_opt(&data.answer, &slur_regex)?;
let actor_keypair = generate_actor_keypair()?; 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")); return Err(LemmyError::from_message("invalid_username"));
} }
let actor_id = generate_local_apub_endpoint( 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 // We have to create both a person, and local_user
// Register the new person // Register the new person
let person_form = PersonForm { let person_form = PersonInsertForm::builder()
name: data.username.to_owned(), .name(data.username.to_owned())
actor_id: Some(actor_id.clone()), .actor_id(Some(actor_id.clone()))
private_key: Some(Some(actor_keypair.private_key)), .private_key(Some(actor_keypair.private_key))
public_key: Some(actor_keypair.public_key), .public_key(actor_keypair.public_key)
inbox_url: Some(generate_inbox_url(&actor_id)?), .inbox_url(Some(generate_inbox_url(&actor_id)?))
shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)), .shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?))
admin: Some(no_admins), .admin(Some(no_admins))
..PersonForm::default() .instance_id(site_view.site.instance_id)
}; .build();
// insert the person // insert the person
let inserted_person = blocking(context.pool(), move |conn| { 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"))?; .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?;
// Create the local user // Create the local user
let local_user_form = LocalUserForm { let local_user_form = LocalUserInsertForm::builder()
person_id: Some(inserted_person.id), .person_id(inserted_person.id)
email: Some(data.email.as_deref().map(|s| s.to_lowercase())), .email(data.email.as_deref().map(|s| s.to_lowercase()))
password_encrypted: Some(data.password.to_string()), .password_encrypted(data.password.to_string())
show_nsfw: Some(data.show_nsfw), .show_nsfw(Some(data.show_nsfw))
email_verified: Some(false), .build();
..LocalUserForm::default()
};
let inserted_local_user = match blocking(context.pool(), move |conn| { let inserted_local_user = match blocking(context.pool(), move |conn| {
LocalUser::register(conn, &local_user_form) LocalUser::create(conn, &local_user_form)
}) })
.await? .await?
{ {
@ -176,13 +168,12 @@ impl PerformCrud for Register {
} }
}; };
if require_application { if local_site.require_application {
// Create the registration application // Create the registration application
let form = RegistrationApplicationForm { let form = RegistrationApplicationInsertForm {
local_user_id: Some(inserted_local_user.id), local_user_id: inserted_local_user.id,
// We already made sure answer was not null above // We already made sure answer was not null above
answer: data.answer.to_owned(), answer: data.answer.to_owned().expect("must have an answer"),
..RegistrationApplicationForm::default()
}; };
blocking(context.pool(), move |conn| { blocking(context.pool(), move |conn| {
@ -192,7 +183,7 @@ impl PerformCrud for Register {
} }
// Email the admins // 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()) send_new_applicant_email_to_admins(&data.username, context.pool(), context.settings())
.await?; .await?;
} }
@ -204,7 +195,7 @@ impl PerformCrud for Register {
}; };
// Log the user in directly if email verification and application aren't required // 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( login_response.jwt = Some(
Claims::jwt( Claims::jwt(
inserted_local_user.id.0, inserted_local_user.id.0,
@ -214,7 +205,7 @@ impl PerformCrud for Register {
.into(), .into(),
); );
} else { } else {
if email_verification { if local_site.require_email_verification {
let local_user_view = LocalUserView { let local_user_view = LocalUserView {
local_user: inserted_local_user, local_user: inserted_local_user,
person: inserted_person, person: inserted_person,
@ -226,12 +217,13 @@ impl PerformCrud for Register {
.email .email
.clone() .clone()
.expect("email was provided"); .expect("email was provided");
send_verification_email(&local_user_view, &email, context.pool(), context.settings()) send_verification_email(&local_user_view, &email, context.pool(), context.settings())
.await?; .await?;
login_response.verify_email_sent = true; login_response.verify_email_sent = true;
} }
if require_application { if local_site.require_application {
login_response.registration_created = true; login_response.registration_created = true;
} }
} }

View file

@ -5,7 +5,10 @@ use lemmy_api_common::{
utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt},
}; };
use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson}; 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::{comment_view::CommentQuery, post_view::PostQuery};
use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe}; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe};
use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_utils::{error::LemmyError, ConnectionId};
@ -31,7 +34,9 @@ impl PerformCrud for GetPersonDetails {
let local_user_view = let local_user_view =
get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
.await?; .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 { let person_details_id = match data.person_id {
Some(id) => id, Some(id) => id,

View file

@ -9,6 +9,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson}, objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::block::block_user::BlockUser, protocol::activities::block::block_user::BlockUser,
@ -33,7 +35,7 @@ use lemmy_db_schema::{
CommunityPersonBanForm, CommunityPersonBanForm,
}, },
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::Person, person::{Person, PersonUpdateForm},
}, },
traits::{Bannable, Crud, Followable}, traits::{Bannable, Crud, Followable},
}; };
@ -123,6 +125,10 @@ impl ActivityHandler for BlockUser {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
match self match self
.target .target
@ -177,7 +183,14 @@ impl ActivityHandler for BlockUser {
match target { match target {
SiteOrCommunity::Site(_site) => { SiteOrCommunity::Site(_site) => {
let blocked_person = blocking(context.pool(), move |conn| { 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??; .await??;
if self.remove_data.unwrap_or(false) { if self.remove_data.unwrap_or(false) {

View file

@ -7,6 +7,8 @@ use crate::{
verify_is_public, verify_is_public,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson}, objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
@ -24,7 +26,7 @@ use lemmy_db_schema::{
source::{ source::{
community::{CommunityPersonBan, CommunityPersonBanForm}, community::{CommunityPersonBan, CommunityPersonBanForm},
moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
person::Person, person::{Person, PersonUpdateForm},
}, },
traits::{Bannable, Crud}, traits::{Bannable, Crud},
}; };
@ -90,6 +92,10 @@ impl ActivityHandler for UndoBlockUser {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
verify_domains_match(self.actor.inner(), self.object.actor.inner())?; verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
@ -121,7 +127,14 @@ impl ActivityHandler for UndoBlockUser {
{ {
SiteOrCommunity::Site(_site) => { SiteOrCommunity::Site(_site) => {
let blocked_person = blocking(context.pool(), move |conn| { 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??; .await??;

View file

@ -12,6 +12,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
generate_moderators_url, generate_moderators_url,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
@ -84,6 +86,10 @@ impl ActivityHandler for AddMod {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;

View file

@ -1,6 +1,8 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_is_public}, activities::{generate_activity_id, send_lemmy_activity, verify_is_public},
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
insert_activity, insert_activity,
objects::community::ApubCommunity, objects::community::ApubCommunity,
protocol::{ protocol::{
@ -11,6 +13,7 @@ use crate::{
}; };
use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
use activitystreams_kinds::{activity::AnnounceType, public}; use activitystreams_kinds::{activity::AnnounceType, public};
use lemmy_api_common::utils::blocking;
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use tracing::debug; use tracing::debug;
@ -84,9 +87,13 @@ impl ActivityHandler for AnnounceActivity {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn verify( async fn verify(
&self, &self,
_context: &Data<LemmyContext>, context: &Data<LemmyContext>,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
Ok(()) Ok(())
} }

View file

@ -12,6 +12,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
generate_moderators_url, generate_moderators_url,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
@ -84,6 +86,10 @@ impl ActivityHandler for RemoveMod {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;

View file

@ -1,5 +1,7 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community}, activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::report::Report, protocol::activities::community::report::Report,
@ -74,6 +76,10 @@ impl ActivityHandler for Report {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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] let community = self.to[0]
.dereference(context, local_instance(context), request_counter) .dereference(context, local_instance(context), request_counter)
.await?; .await?;

View file

@ -7,6 +7,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::community::update::UpdateCommunity, protocol::activities::community::update::UpdateCommunity,
@ -19,10 +21,7 @@ use activitypub_federation::{
}; };
use activitystreams_kinds::{activity::UpdateType, public}; use activitystreams_kinds::{activity::UpdateType, public};
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{source::community::Community, traits::Crud};
source::community::{Community, CommunityForm},
traits::Crud,
};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
use url::Url; use url::Url;
@ -72,6 +71,9 @@ impl ActivityHandler for UpdateCommunity {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &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> { ) -> Result<(), LemmyError> {
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
let updated_community = self.object.into_form(); let community_update_form = self.object.into_update_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 updated_community = blocking(context.pool(), move |conn| { let updated_community = blocking(context.pool(), move |conn| {
Community::update(conn, community.id, &cf) Community::update(conn, community.id, &community_update_form)
}) })
.await??; .await??;

View file

@ -8,6 +8,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
mentions::MentionOrValue, mentions::MentionOrValue,
objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
@ -115,6 +117,10 @@ impl ActivityHandler for CreateOrUpdateComment {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
let post = self.object.get_parents(context, request_counter).await?.0; let post = self.object.get_parents(context, request_counter).await?.0;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;

View file

@ -8,6 +8,8 @@ use crate::{
verify_person_in_community, verify_person_in_community,
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType}, protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
ActorType, ActorType,
@ -93,6 +95,9 @@ impl ActivityHandler for CreateOrUpdatePost {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &self.cc)?;
let community = self.get_community(context, request_counter).await?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?;

View file

@ -1,5 +1,7 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person}, activities::{generate_activity_id, send_lemmy_activity, verify_person},
check_apub_id_valid,
fetch_local_site_data,
objects::{person::ApubPerson, private_message::ApubPrivateMessage}, objects::{person::ApubPerson, private_message::ApubPrivateMessage},
protocol::activities::{ protocol::activities::{
create_or_update::private_message::CreateOrUpdatePrivateMessage, create_or_update::private_message::CreateOrUpdatePrivateMessage,
@ -69,6 +71,10 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_person(&self.actor, context, request_counter).await?;
verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_domains_match(self.actor.inner(), self.object.id.inner())?;
verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?; verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;

View file

@ -4,6 +4,8 @@ use crate::{
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id, generate_activity_id,
}, },
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::{activities::deletion::delete::Delete, IdOrNestedObject}, protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
@ -14,8 +16,8 @@ use anyhow::anyhow;
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::{Comment, CommentUpdateForm},
community::Community, community::{Community, CommunityUpdateForm},
moderator::{ moderator::{
ModRemoveComment, ModRemoveComment,
ModRemoveCommentForm, ModRemoveCommentForm,
@ -24,7 +26,7 @@ use lemmy_db_schema::{
ModRemovePost, ModRemovePost,
ModRemovePostForm, ModRemovePostForm,
}, },
post::Post, post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -55,6 +57,9 @@ impl ActivityHandler for Delete {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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?; verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
Ok(()) Ok(())
} }
@ -150,7 +155,11 @@ pub(in crate::activities) async fn receive_remove_action(
}) })
.await??; .await??;
let deleted_community = blocking(context.pool(), move |conn| { 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??; .await??;
@ -168,7 +177,11 @@ pub(in crate::activities) async fn receive_remove_action(
}) })
.await??; .await??;
let removed_post = blocking(context.pool(), move |conn| { 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??; .await??;
@ -186,7 +199,11 @@ pub(in crate::activities) async fn receive_remove_action(
}) })
.await??; .await??;
let removed_comment = blocking(context.pool(), move |conn| { 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??; .await??;

View file

@ -1,5 +1,7 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person}, activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{instance::remote_instance_inboxes, person::ApubPerson}, objects::{instance::remote_instance_inboxes, person::ApubPerson},
protocol::activities::deletion::delete_user::DeleteUser, protocol::activities::deletion::delete_user::DeleteUser,
@ -11,7 +13,7 @@ use activitypub_federation::{
utils::verify_urls_match, utils::verify_urls_match,
}; };
use activitystreams_kinds::{activity::DeleteType, public}; 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_utils::error::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -36,6 +38,9 @@ impl ActivityHandler for DeleteUser {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_is_public(&self.to, &[])?;
verify_person(&self.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
verify_urls_match(self.actor.inner(), self.object.inner())?; verify_urls_match(self.actor.inner(), self.object.inner())?;

View file

@ -28,11 +28,11 @@ use activitystreams_kinds::public;
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::{Comment, CommentUpdateForm},
community::Community, community::{Community, CommunityUpdateForm},
person::Person, person::Person,
post::Post, post::{Post, PostUpdateForm},
private_message::PrivateMessage, private_message::{PrivateMessage, PrivateMessageUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -239,7 +239,13 @@ async fn receive_delete_action(
} }
let community = blocking(context.pool(), move |conn| { 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??; .await??;
send_community_ws_message( send_community_ws_message(
@ -254,7 +260,11 @@ async fn receive_delete_action(
DeletableObjects::Post(post) => { DeletableObjects::Post(post) => {
if deleted != post.deleted { if deleted != post.deleted {
let deleted_post = blocking(context.pool(), move |conn| { 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??; .await??;
send_post_ws_message( send_post_ws_message(
@ -270,7 +280,11 @@ async fn receive_delete_action(
DeletableObjects::Comment(comment) => { DeletableObjects::Comment(comment) => {
if deleted != comment.deleted { if deleted != comment.deleted {
let deleted_comment = blocking(context.pool(), move |conn| { 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??; .await??;
send_comment_ws_message_simple( send_comment_ws_message_simple(
@ -283,7 +297,13 @@ async fn receive_delete_action(
} }
DeletableObjects::PrivateMessage(pm) => { DeletableObjects::PrivateMessage(pm) => {
let deleted_private_message = blocking(context.pool(), move |conn| { 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??; .await??;

View file

@ -4,6 +4,8 @@ use crate::{
deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
generate_activity_id, generate_activity_id,
}, },
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, 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_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
comment::Comment, comment::{Comment, CommentUpdateForm},
community::Community, community::{Community, CommunityUpdateForm},
moderator::{ moderator::{
ModRemoveComment, ModRemoveComment,
ModRemoveCommentForm, ModRemoveCommentForm,
@ -23,7 +25,7 @@ use lemmy_db_schema::{
ModRemovePost, ModRemovePost,
ModRemovePostForm, ModRemovePostForm,
}, },
post::Post, post::{Post, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -54,6 +56,9 @@ impl ActivityHandler for UndoDelete {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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?; self.object.verify(context, request_counter).await?;
verify_delete_activity( verify_delete_activity(
&self.object, &self.object,
@ -148,7 +153,11 @@ impl UndoDelete {
}) })
.await??; .await??;
let deleted_community = blocking(context.pool(), move |conn| { 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??; .await??;
send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?; send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?;
@ -165,7 +174,11 @@ impl UndoDelete {
}) })
.await??; .await??;
let removed_post = blocking(context.pool(), move |conn| { 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??; .await??;
send_post_ws_message(removed_post.id, EditPost, None, None, context).await?; send_post_ws_message(removed_post.id, EditPost, None, None, context).await?;
@ -182,7 +195,11 @@ impl UndoDelete {
}) })
.await??; .await??;
let removed_comment = blocking(context.pool(), move |conn| { 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??; .await??;
send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?; send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?;

View file

@ -1,5 +1,7 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity}, activities::{generate_activity_id, send_lemmy_activity},
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
ActorType, ActorType,
@ -67,6 +69,10 @@ impl ActivityHandler for AcceptFollowCommunity {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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())?; verify_urls_match(self.actor.inner(), self.object.object.inner())?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;
Ok(()) Ok(())

View file

@ -5,6 +5,8 @@ use crate::{
verify_person, verify_person,
verify_person_in_community, verify_person_in_community,
}, },
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
@ -84,6 +86,9 @@ impl ActivityHandler for FollowCommunity {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_person(&self.actor, context, request_counter).await?;
let community = self let community = self
.object .object

View file

@ -1,5 +1,7 @@
use crate::{ use crate::{
activities::{generate_activity_id, send_lemmy_activity, verify_person}, activities::{generate_activity_id, send_lemmy_activity, verify_person},
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity}, protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
@ -63,6 +65,9 @@ impl ActivityHandler for UndoFollowCommunity {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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_urls_match(self.actor.inner(), self.object.actor.inner())?;
verify_person(&self.actor, context, request_counter).await?; verify_person(&self.actor, context, request_counter).await?;
self.object.verify(context, request_counter).await?; self.object.verify(context, request_counter).await?;

View file

@ -14,7 +14,10 @@ use activitypub_federation::{
use activitystreams_kinds::public; use activitystreams_kinds::public;
use anyhow::anyhow; use anyhow::anyhow;
use lemmy_api_common::utils::blocking; 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_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
@ -167,9 +170,14 @@ where
ActorT: Actor + ActorType, ActorT: Actor + ActorType,
Activity: ActivityHandler<Error = LemmyError>, 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(()); return Ok(());
} }
info!("Sending activity {}", activity.id().to_string()); info!("Sending activity {}", activity.id().to_string());
let activity = WithContext::new(activity, CONTEXT.deref().clone()); let activity = WithContext::new(activity, CONTEXT.deref().clone());

View file

@ -6,6 +6,8 @@ use crate::{
voting::{undo_vote_comment, undo_vote_post}, voting::{undo_vote_comment, undo_vote_post},
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::voting::{ protocol::activities::voting::{
@ -82,6 +84,9 @@ impl ActivityHandler for UndoVote {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &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())?; verify_urls_match(self.actor.inner(), self.object.actor.inner())?;

View file

@ -6,6 +6,8 @@ use crate::{
voting::{vote_comment, vote_post}, voting::{vote_comment, vote_post},
}, },
activity_lists::AnnouncableActivities, activity_lists::AnnouncableActivities,
check_apub_id_valid,
fetch_local_site_data,
local_instance, local_instance,
objects::{community::ApubCommunity, person::ApubPerson}, objects::{community::ApubCommunity, person::ApubPerson},
protocol::activities::voting::vote::{Vote, VoteType}, protocol::activities::voting::vote::{Vote, VoteType},
@ -17,7 +19,7 @@ use anyhow::anyhow;
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
newtypes::CommunityId, newtypes::CommunityId,
source::{community::Community, post::Post, site::Site}, source::{community::Community, local_site::LocalSite, post::Post},
traits::Crud, traits::Crud,
}; };
use lemmy_utils::error::LemmyError; use lemmy_utils::error::LemmyError;
@ -81,10 +83,16 @@ impl ActivityHandler for Vote {
context: &Data<LemmyContext>, context: &Data<LemmyContext>,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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?; let community = self.get_community(context, request_counter).await?;
verify_person_in_community(&self.actor, &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??; let enable_downvotes = blocking(context.pool(), LocalSite::read)
if self.kind == VoteType::Dislike && !site.enable_downvotes { .await?
.map(|l| l.enable_downvotes)
.unwrap_or(true);
if self.kind == VoteType::Dislike && !enable_downvotes {
return Err(anyhow!("Downvotes disabled").into()); return Err(anyhow!("Downvotes disabled").into());
} }
Ok(()) Ok(())

View file

@ -152,7 +152,8 @@ mod tests {
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
community::Community, community::Community,
person::{Person, PersonForm}, instance::Instance,
person::{Person, PersonInsertForm},
site::Site, site::Site,
}, },
traits::Crud, traits::Crud,
@ -168,11 +169,14 @@ mod tests {
let community = parse_lemmy_community(&context).await; let community = parse_lemmy_community(&context).await;
let community_id = community.id; let community_id = community.id;
let old_mod = PersonForm { let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap();
name: "holly".into(),
public_key: Some("pubkey".to_string()), let old_mod = PersonInsertForm::builder()
..PersonForm::default() .name("holly".into())
}; .public_key("pubkey".to_string())
.instance_id(inserted_instance.id)
.build();
let old_mod = Person::create(conn, &old_mod).unwrap(); let old_mod = Person::create(conn, &old_mod).unwrap();
let community_moderator_form = CommunityModeratorForm { let community_moderator_form = CommunityModeratorForm {
community_id: community.id, community_id: community.id,
@ -210,5 +214,6 @@ mod tests {
Person::delete(conn, new_mod.id).unwrap(); Person::delete(conn, new_mod.id).unwrap();
Community::delete(conn, community_context.0.id).unwrap(); Community::delete(conn, community_context.0.id).unwrap();
Site::delete(conn, site.id).unwrap(); Site::delete(conn, site.id).unwrap();
Instance::delete(conn, inserted_instance.id).unwrap();
} }
} }

View file

@ -2,7 +2,8 @@ use crate::{local_instance, ActorType};
use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject};
use anyhow::anyhow; use anyhow::anyhow;
use itertools::Itertools; 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_utils::error::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
@ -47,8 +48,14 @@ where
); );
debug!("Fetching webfinger url: {}", &fetch_url); 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; *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")); return Err(LemmyError::from_message("Request retry limit reached"));
} }

View file

@ -19,51 +19,46 @@ use actix_web::{
web, web,
}; };
use http_signature_normalization_actix::digest::middleware::VerifyDigest; use http_signature_normalization_actix::digest::middleware::VerifyDigest;
use lemmy_utils::settings::structs::Settings;
use sha2::{Digest, Sha256}; use sha2::{Digest, Sha256};
pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) { pub fn config(cfg: &mut web::ServiceConfig) {
if settings.federation.enabled { cfg
println!("federation enabled, host is {}", settings.hostname); .route("/", web::get().to(get_apub_site_http))
.route("/site_outbox", web::get().to(get_apub_site_outbox))
.route(
"/c/{community_name}",
web::get().to(get_apub_community_http),
)
.route(
"/c/{community_name}/followers",
web::get().to(get_apub_community_followers),
)
.route(
"/c/{community_name}/outbox",
web::get().to(get_apub_community_outbox),
)
.route(
"/c/{community_name}/moderators",
web::get().to(get_apub_community_moderators),
)
.route("/u/{user_name}", web::get().to(get_apub_person_http))
.route(
"/u/{user_name}/outbox",
web::get().to(get_apub_person_outbox),
)
.route("/post/{post_id}", web::get().to(get_apub_post))
.route("/comment/{comment_id}", web::get().to(get_apub_comment))
.route("/activities/{type_}/{id}", web::get().to(get_activity));
cfg cfg.service(
.route("/", web::get().to(get_apub_site_http)) web::scope("")
.route("/site_outbox", web::get().to(get_apub_site_outbox)) .wrap(VerifyDigest::new(Sha256::new()))
.route( .guard(InboxRequestGuard)
"/c/{community_name}", .route("/c/{community_name}/inbox", web::post().to(community_inbox))
web::get().to(get_apub_community_http), .route("/u/{user_name}/inbox", web::post().to(person_inbox))
) .route("/inbox", web::post().to(shared_inbox))
.route( .route("/site_inbox", web::post().to(get_apub_site_inbox)),
"/c/{community_name}/followers", );
web::get().to(get_apub_community_followers),
)
.route(
"/c/{community_name}/outbox",
web::get().to(get_apub_community_outbox),
)
.route(
"/c/{community_name}/moderators",
web::get().to(get_apub_community_moderators),
)
.route("/u/{user_name}", web::get().to(get_apub_person_http))
.route(
"/u/{user_name}/outbox",
web::get().to(get_apub_person_outbox),
)
.route("/post/{post_id}", web::get().to(get_apub_post))
.route("/comment/{comment_id}", web::get().to(get_apub_comment))
.route("/activities/{type_}/{id}", web::get().to(get_activity));
cfg.service(
web::scope("")
.wrap(VerifyDigest::new(Sha256::new()))
.guard(InboxRequestGuard)
.route("/c/{community_name}/inbox", web::post().to(community_inbox))
.route("/u/{user_name}/inbox", web::post().to(person_inbox))
.route("/inbox", web::post().to(shared_inbox))
.route("/site_inbox", web::post().to(get_apub_site_inbox)),
);
}
} }
/// Without this, things like webfinger or RSS feeds stop working, as all requests seem to get /// Without this, things like webfinger or RSS feeds stop working, as all requests seem to get

View file

@ -7,7 +7,7 @@ use crate::{
use activitypub_federation::{deser::context::WithContext, traits::ApubObject}; use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
use actix_web::{web, HttpRequest, HttpResponse}; use actix_web::{web, HttpRequest, HttpResponse};
use lemmy_api_common::utils::blocking; 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_utils::error::LemmyError;
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use url::Url; use url::Url;
@ -15,7 +15,10 @@ use url::Url;
pub(crate) async fn get_apub_site_http( pub(crate) async fn get_apub_site_http(
context: web::Data<LemmyContext>, context: web::Data<LemmyContext>,
) -> Result<HttpResponse, LemmyError> { ) -> 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?; let apub = site.into_apub(&context).await?;
Ok(create_apub_response(&apub)) Ok(create_apub_response(&apub))

View file

@ -6,13 +6,14 @@ use activitypub_federation::{
LocalInstance, LocalInstance,
}; };
use anyhow::Context; use anyhow::Context;
use diesel::PgConnection;
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool}; use lemmy_db_schema::{
use lemmy_utils::{ newtypes::DbUrl,
error::LemmyError, source::{activity::Activity, instance::Instance, local_site::LocalSite},
location_info, utils::DbPool,
settings::{structs::Settings, SETTINGS},
}; };
use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
use lemmy_websocket::LemmyContext; use lemmy_websocket::LemmyContext;
use once_cell::sync::{Lazy, OnceCell}; use once_cell::sync::{Lazy, OnceCell};
use url::{ParseError, Url}; 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: 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 { fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::new(); static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::new();
LOCAL_INSTANCE.get_or_init(|| { 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() let settings = InstanceSettings::builder()
.http_fetch_retry_limit(context.settings().federation.http_fetch_retry_limit) .http_fetch_retry_limit(http_fetch_retry_limit)
.worker_count(context.settings().federation.worker_count) .worker_count(worker_count)
.debug(context.settings().federation.debug) .debug(federation_debug)
// TODO No idea why, but you can't pass context.settings() to the verify_url_function closure // TODO No idea why, but you can't pass context.settings() to the verify_url_function closure
// without the value getting captured. // without the value getting captured.
.verify_url_function(|url| check_apub_id_valid(url, &SETTINGS))
.http_signature_compat(true) .http_signature_compat(true)
.build() .build()
.expect("configure federation"); .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 /// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
/// post/comment in a local community. /// post/comment in a local community.
#[tracing::instrument(skip(settings))] #[tracing::instrument(skip(settings, local_site_data))]
fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'static str> { // 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 domain = apub_id.domain().expect("apud id has domain").to_string();
let local_instance = settings let local_instance = settings
.get_hostname_without_port() .get_hostname_without_port()
@ -72,7 +97,12 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
return Ok(()); 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"); 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"); 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) { if blocked.contains(&domain) {
return Err("Domain is blocked"); 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) { if !allowed.contains(&domain) {
return Err("Domain is not in allowlist"); return Err("Domain is not in allowlist");
} }
@ -95,13 +125,40 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
Ok(()) 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( pub(crate) fn check_apub_id_valid_with_strictness(
apub_id: &Url, apub_id: &Url,
is_strict: bool, is_strict: bool,
local_site_data: &LocalSiteData,
settings: &Settings, settings: &Settings,
) -> Result<(), LemmyError> { ) -> 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 domain = apub_id.domain().expect("apud id has domain").to_string();
let local_instance = settings let local_instance = settings
.get_hostname_without_port() .get_hostname_without_port()
@ -110,15 +167,20 @@ pub(crate) fn check_apub_id_valid_with_strictness(
return Ok(()); 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. // 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 { if is_strict || strict_allowlist {
// need to allow this explicitly because apub receive might contain objects from our local // need to allow this explicitly because apub receive might contain objects from our local
// instance. // 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( return Err(LemmyError::from_message(
"Federation forbidden by strict allowlist", "Federation forbidden by strict allowlist",
)); ));
@ -203,7 +265,7 @@ async fn insert_activity(
let ap_id = ap_id.to_owned().into(); let ap_id = ap_id.to_owned().into();
Ok( Ok(
blocking(pool, move |conn| { blocking(pool, move |conn| {
Activity::insert(conn, ap_id, activity, local, sensitive) Activity::insert(conn, ap_id, activity, local, Some(sensitive))
}) })
.await??, .await??,
) )

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
activities::{verify_is_public, verify_person_in_community}, activities::{verify_is_public, verify_person_in_community},
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
fetch_local_site_data,
local_instance, local_instance,
mentions::collect_non_local_mentions, mentions::collect_non_local_mentions,
objects::{read_from_string_or_source, verify_is_remote_object}, objects::{read_from_string_or_source, verify_is_remote_object},
@ -18,11 +19,12 @@ use activitypub_federation::{
}; };
use activitystreams_kinds::{object::NoteType, public}; use activitystreams_kinds::{object::NoteType, public};
use chrono::NaiveDateTime; 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::{ use lemmy_db_schema::{
source::{ source::{
comment::{Comment, CommentForm}, comment::{Comment, CommentInsertForm, CommentUpdateForm},
community::Community, community::Community,
local_site::LocalSite,
person::Person, person::Person,
post::Post, post::Post,
}, },
@ -81,7 +83,8 @@ impl ApubObject for ApubComment {
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
if !self.deleted { if !self.deleted {
blocking(context.pool(), move |conn| { 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??; .await??;
} }
@ -148,7 +151,14 @@ impl ApubObject for ApubComment {
Community::read(conn, community_id) Community::read(conn, community_id)
}) })
.await??; .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_is_remote_object(note.id.inner(), context.settings())?;
verify_person_in_community( verify_person_in_community(
&note.attributed_to, &note.attributed_to,
@ -179,10 +189,13 @@ impl ApubObject for ApubComment {
let (post, parent_comment) = note.get_parents(context, request_counter).await?; let (post, parent_comment) = note.get_parents(context, request_counter).await?;
let content = read_from_string_or_source(&note.content, &note.media_type, &note.source); let content = read_from_string_or_source(&note.content, &note.media_type, &note.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 language_id = LanguageTag::to_language_id_single(note.language, context.pool()).await?;
let form = CommentForm { let form = CommentInsertForm {
creator_id: creator.id, creator_id: creator.id,
post_id: post.id, post_id: post.id,
content: content_slurs_removed, content: content_slurs_removed,
@ -244,6 +257,7 @@ pub(crate) mod tests {
Community::delete(conn, data.1.id).unwrap(); Community::delete(conn, data.1.id).unwrap();
Person::delete(conn, data.0.id).unwrap(); Person::delete(conn, data.0.id).unwrap();
Site::delete(conn, data.3.id).unwrap(); Site::delete(conn, data.3.id).unwrap();
LocalSite::delete(conn).unwrap();
} }
#[actix_rt::test] #[actix_rt::test]

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
collections::{community_moderators::ApubCommunityModerators, CommunityContext}, collections::{community_moderators::ApubCommunityModerators, CommunityContext},
fetch_local_site_data,
generate_moderators_url, generate_moderators_url,
generate_outbox_url, generate_outbox_url,
local_instance, local_instance,
@ -21,8 +22,12 @@ use chrono::NaiveDateTime;
use itertools::Itertools; use itertools::Itertools;
use lemmy_api_common::utils::blocking; use lemmy_api_common::utils::blocking;
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{actor_language::CommunityLanguage, community::Community}, source::{
traits::ApubActor, actor_language::CommunityLanguage,
community::{Community, CommunityUpdateForm},
instance::Instance,
},
traits::{ApubActor, Crud},
}; };
use lemmy_db_views_actor::structs::CommunityFollowerView; use lemmy_db_views_actor::structs::CommunityFollowerView;
use lemmy_utils::{ use lemmy_utils::{
@ -78,7 +83,8 @@ impl ApubObject for ApubCommunity {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| { 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??; .await??;
Ok(()) Ok(())
@ -138,11 +144,17 @@ impl ApubObject for ApubCommunity {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<ApubCommunity, LemmyError> { ) -> 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 languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
let community: ApubCommunity = blocking(context.pool(), move |conn| { 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)?; CommunityLanguage::update(conn, languages, community.id)?;
Ok::<Community, diesel::result::Error>(community) Ok::<Community, diesel::result::Error>(community)
}) })
@ -205,6 +217,7 @@ impl ApubCommunity {
) -> Result<Vec<Url>, LemmyError> { ) -> Result<Vec<Url>, LemmyError> {
let id = self.id; let id = self.id;
let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
let follows = blocking(context.pool(), move |conn| { let follows = blocking(context.pool(), move |conn| {
CommunityFollowerView::for_community(conn, id) CommunityFollowerView::for_community(conn, id)
}) })
@ -221,7 +234,10 @@ impl ApubCommunity {
.unique() .unique()
.filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname)) .filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname))
// Don't send to blocked instances // 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(); .collect();
Ok(inboxes) Ok(inboxes)

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
fetch_local_site_data,
local_instance, local_instance,
objects::read_from_string_or_source_opt, objects::read_from_string_or_source_opt,
protocol::{ protocol::{
@ -19,12 +20,14 @@ use activitypub_federation::{
utils::verify_domains_match, utils::verify_domains_match,
}; };
use chrono::NaiveDateTime; 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::{ use lemmy_db_schema::{
source::{ source::{
actor_language::SiteLanguage, actor_language::SiteLanguage,
site::{Site, SiteForm}, instance::Instance as DbInstance,
site::{Site, SiteInsertForm},
}, },
traits::Crud,
utils::{naive_now, DbPool}, utils::{naive_now, DbPool},
}; };
use lemmy_utils::{ use lemmy_utils::{
@ -114,10 +117,13 @@ impl ApubObject for ApubSite {
data: &Self::DataType, data: &Self::DataType,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> 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())?; 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(&apub.name, slur_regex)?;
check_slurs_opt(&apub.summary, slur_regex)?; check_slurs_opt(&apub.summary, slur_regex)?;
Ok(()) Ok(())
@ -129,27 +135,30 @@ impl ApubObject for ApubSite {
data: &Self::DataType, data: &Self::DataType,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<Self, LemmyError> { ) -> 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(), name: apub.name.clone(),
sidebar: Some(read_from_string_or_source_opt( sidebar: read_from_string_or_source_opt(&apub.content, &None, &apub.source),
&apub.content,
&None,
&apub.source,
)),
updated: apub.updated.map(|u| u.clone().naive_local()), updated: apub.updated.map(|u| u.clone().naive_local()),
icon: Some(apub.icon.clone().map(|i| i.url.into())), icon: apub.icon.clone().map(|i| i.url.into()),
banner: Some(apub.image.clone().map(|i| i.url.into())), banner: apub.image.clone().map(|i| i.url.into()),
description: Some(apub.summary.clone()), description: apub.summary.clone(),
actor_id: Some(apub.id.clone().into()), actor_id: Some(apub.id.clone().into()),
last_refreshed_at: Some(naive_now()), last_refreshed_at: Some(naive_now()),
inbox_url: Some(apub.inbox.clone().into()), inbox_url: Some(apub.inbox.clone().into()),
public_key: Some(apub.public_key.public_key_pem.clone()), 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 languages = LanguageTag::to_language_id_multiple(apub.language, data.pool()).await?;
let site = blocking(data.pool(), move |conn| { 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)?; SiteLanguage::update(conn, languages, site.id)?;
Ok::<Site, diesel::result::Error>(site) Ok::<Site, diesel::result::Error>(site)
}) })

View file

@ -67,7 +67,7 @@ pub(crate) mod tests {
}; };
use lemmy_utils::{ use lemmy_utils::{
error::LemmyError, error::LemmyError,
rate_limit::{rate_limiter::RateLimiter, RateLimit}, rate_limit::{rate_limiter::RateLimiter, RateLimit, RateLimitConfig},
settings::SETTINGS, settings::SETTINGS,
}; };
use lemmy_websocket::{chat_server::ChatServer, LemmyContext}; use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
@ -96,10 +96,6 @@ pub(crate) mod tests {
// call this to run migrations // call this to run migrations
establish_unpooled_connection(); establish_unpooled_connection();
let settings = SETTINGS.to_owned(); 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() let client = Client::builder()
.user_agent(build_user_agent(&settings)) .user_agent(build_user_agent(&settings))
.build() .build()
@ -122,6 +118,14 @@ pub(crate) mod tests {
async fn x() -> Result<String, LemmyError> { async fn x() -> Result<String, LemmyError> {
Ok("".to_string()) 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( let chat_server = ChatServer::startup(
pool.clone(), pool.clone(),
rate_limiter, rate_limiter,

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
fetch_local_site_data,
generate_outbox_url, generate_outbox_url,
objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt}, objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
protocol::{ protocol::{
@ -18,10 +19,13 @@ use activitypub_federation::{
utils::verify_domains_match, utils::verify_domains_match,
}; };
use chrono::NaiveDateTime; 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::{ use lemmy_db_schema::{
source::person::{Person as DbPerson, PersonForm}, source::{
traits::ApubActor, instance::Instance,
person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
},
traits::{ApubActor, Crud},
utils::naive_now, utils::naive_now,
}; };
use lemmy_utils::{ use lemmy_utils::{
@ -76,7 +80,8 @@ impl ApubObject for ApubPerson {
#[tracing::instrument(skip_all)] #[tracing::instrument(skip_all)]
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
blocking(context.pool(), move |conn| { 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??; .await??;
Ok(()) Ok(())
@ -119,12 +124,20 @@ impl ApubObject for ApubPerson {
context: &LemmyContext, context: &LemmyContext,
_request_counter: &mut i32, _request_counter: &mut i32,
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(person.id.inner(), expected_domain)?; let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
check_apub_id_valid_with_strictness(person.id.inner(), false, context.settings())?; 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(&person.preferred_username, slur_regex)?;
check_slurs_opt(&person.name, 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); let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
check_slurs_opt(&bio, slur_regex)?; check_slurs_opt(&bio, slur_regex)?;
Ok(()) Ok(())
@ -136,34 +149,37 @@ impl ApubObject for ApubPerson {
context: &LemmyContext, context: &LemmyContext,
request_counter: &mut i32, request_counter: &mut i32,
) -> Result<ApubPerson, LemmyError> { ) -> 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, name: person.preferred_username,
display_name: Some(person.name), display_name: person.name,
banned: None, banned: None,
ban_expires: None, ban_expires: None,
deleted: None, deleted: None,
avatar: Some(person.icon.map(|i| i.url.into())), avatar: person.icon.map(|i| i.url.into()),
banner: Some(person.image.map(|i| i.url.into())), banner: person.image.map(|i| i.url.into()),
published: person.published.map(|u| u.naive_local()), published: person.published.map(|u| u.naive_local()),
updated: person.updated.map(|u| u.naive_local()), updated: person.updated.map(|u| u.naive_local()),
actor_id: Some(person.id.into()), actor_id: Some(person.id.into()),
bio: Some(read_from_string_or_source_opt( bio: read_from_string_or_source_opt(&person.summary, &None, &person.source),
&person.summary,
&None,
&person.source,
)),
local: Some(false), local: Some(false),
admin: Some(false), admin: Some(false),
bot_account: Some(person.kind == UserTypes::Service), bot_account: Some(person.kind == UserTypes::Service),
private_key: None, 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()), last_refreshed_at: Some(naive_now()),
inbox_url: Some(person.inbox.into()), inbox_url: Some(person.inbox.into()),
shared_inbox_url: Some(person.endpoints.map(|e| e.shared_inbox.into())), shared_inbox_url: person.endpoints.map(|e| e.shared_inbox.into()),
matrix_user_id: Some(person.matrix_user_id), matrix_user_id: person.matrix_user_id,
instance_id: instance.id,
}; };
let person = blocking(context.pool(), move |conn| { let person = blocking(context.pool(), move |conn| {
DbPerson::upsert(conn, &person_form) DbPerson::create(conn, &person_form)
}) })
.await??; .await??;
@ -230,22 +246,19 @@ pub(crate) mod tests {
#[serial] #[serial]
async fn test_parse_lemmy_person() { async fn test_parse_lemmy_person() {
let context = init_context(); let context = init_context();
let conn = &mut context.pool().get().unwrap();
let (person, site) = parse_lemmy_person(&context).await; let (person, site) = parse_lemmy_person(&context).await;
assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string())); assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string()));
assert!(!person.local); assert!(!person.local);
assert_eq!(person.bio.as_ref().unwrap().len(), 39); assert_eq!(person.bio.as_ref().unwrap().len(), 39);
DbPerson::delete(conn, person.id).unwrap(); cleanup((person, site), &context);
Site::delete(conn, site.id).unwrap();
} }
#[actix_rt::test] #[actix_rt::test]
#[serial] #[serial]
async fn test_parse_pleroma_person() { async fn test_parse_pleroma_person() {
let context = init_context(); 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 // 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(); 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!(request_counter, 0);
assert_eq!(person.bio.as_ref().unwrap().len(), 873); assert_eq!(person.bio.as_ref().unwrap().len(), 873);
DbPerson::delete(conn, person.id).unwrap(); cleanup((person, site), &context);
Site::delete(conn, site.id).unwrap(); }
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();
} }
} }

View file

@ -1,6 +1,7 @@
use crate::{ use crate::{
activities::{verify_is_public, verify_person_in_community}, activities::{verify_is_public, verify_person_in_community},
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
fetch_local_site_data,
local_instance, local_instance,
objects::{read_from_string_or_source_opt, verify_is_remote_object}, objects::{read_from_string_or_source_opt, verify_is_remote_object},
protocol::{ protocol::{
@ -20,14 +21,18 @@ use activitypub_federation::{
}; };
use activitystreams_kinds::public; use activitystreams_kinds::public;
use chrono::NaiveDateTime; 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::{ use lemmy_db_schema::{
self, self,
source::{ source::{
community::Community, community::Community,
local_site::LocalSite,
moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm}, moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm},
person::Person, person::Person,
post::{Post, PostForm}, post::{Post, PostInsertForm, PostUpdateForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -84,7 +89,8 @@ impl ApubObject for ApubPost {
async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
if !self.deleted { if !self.deleted {
blocking(context.pool(), move |conn| { 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??; .await??;
} }
@ -140,10 +146,20 @@ impl ApubObject for ApubPost {
verify_is_remote_object(page.id.inner(), context.settings())?; 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?; 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?; 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_domains_match(page.creator()?.inner(), page.id.inner())?;
verify_is_public(&page.to, &page.cc)?; verify_is_public(&page.to, &page.cc)?;
Ok(()) Ok(())
@ -181,16 +197,19 @@ impl ApubObject for ApubPost {
(None, page.image.map(|i| i.url.into())) (None, page.image.map(|i| i.url.into()))
}; };
let (embed_title, embed_description, embed_video_url) = metadata_res 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(); .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 = let body_slurs_removed =
read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) 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?; let language_id = LanguageTag::to_language_id_single(page.language, context.pool()).await?;
PostForm { PostInsertForm {
name: page.name.clone(), name: page.name.clone(),
url: Some(url.map(Into::into)), url: url.map(Into::into),
body: body_slurs_removed, body: body_slurs_removed,
creator_id: creator.id, creator_id: creator.id,
community_id: community.id, community_id: community.id,
@ -204,23 +223,22 @@ impl ApubObject for ApubPost {
embed_title, embed_title,
embed_description, embed_description,
embed_video_url, embed_video_url,
thumbnail_url: Some(thumbnail_url), thumbnail_url,
ap_id: Some(page.id.clone().into()), ap_id: Some(page.id.clone().into()),
local: Some(false), local: Some(false),
language_id, language_id,
} }
} else { } else {
// if is mod action, only update locked/stickied fields, nothing else // if is mod action, only update locked/stickied fields, nothing else
PostForm { PostInsertForm::builder()
name: page.name.clone(), .name(page.name.clone())
creator_id: creator.id, .creator_id(creator.id)
community_id: community.id, .community_id(community.id)
locked: page.comments_enabled.map(|e| !e), .ap_id(Some(page.id.clone().into()))
stickied: page.stickied, .locked(page.comments_enabled.map(|e| !e))
updated: page.updated.map(|u| u.naive_local()), .stickied(page.stickied)
ap_id: Some(page.id.clone().into()), .updated(page.updated.map(|u| u.naive_local()))
..Default::default() .build()
}
}; };
// read existing, local post if any (for generating mod log) // read existing, local post if any (for generating mod log)
@ -228,7 +246,7 @@ impl ApubObject for ApubPost {
.dereference_local(context) .dereference_local(context)
.await; .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 // write mod log entries for sticky/lock
if Page::is_stickied_changed(&old_post, &page.stickied) { if Page::is_stickied_changed(&old_post, &page.stickied) {

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
check_apub_id_valid_with_strictness, check_apub_id_valid_with_strictness,
fetch_local_site_data,
local_instance, local_instance,
objects::read_from_string_or_source, objects::read_from_string_or_source,
protocol::{ protocol::{
@ -18,7 +19,7 @@ use lemmy_api_common::utils::{blocking, check_person_block};
use lemmy_db_schema::{ use lemmy_db_schema::{
source::{ source::{
person::Person, person::Person,
private_message::{PrivateMessage, PrivateMessageForm}, private_message::{PrivateMessage, PrivateMessageInsertForm},
}, },
traits::Crud, traits::Crud,
}; };
@ -108,7 +109,15 @@ impl ApubObject for ApubPrivateMessage {
) -> Result<(), LemmyError> { ) -> Result<(), LemmyError> {
verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.id.inner(), expected_domain)?;
verify_domains_match(note.attributed_to.inner(), note.id.inner())?; 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 let person = note
.attributed_to .attributed_to
.dereference(context, local_instance(context), request_counter) .dereference(context, local_instance(context), request_counter)
@ -134,7 +143,7 @@ impl ApubObject for ApubPrivateMessage {
.await?; .await?;
check_person_block(creator.id, recipient.id, context.pool()).await?; check_person_block(creator.id, recipient.id, context.pool()).await?;
let form = PrivateMessageForm { let form = PrivateMessageInsertForm {
creator_id: creator.id, creator_id: creator.id,
recipient_id: recipient.id, recipient_id: recipient.id,
content: read_from_string_or_source(&note.content, &None, &note.source), content: read_from_string_or_source(&note.content, &None, &note.source),
@ -146,7 +155,7 @@ impl ApubObject for ApubPrivateMessage {
local: Some(false), local: Some(false),
}; };
let pm = blocking(context.pool(), move |conn| { let pm = blocking(context.pool(), move |conn| {
PrivateMessage::upsert(conn, &form) PrivateMessage::create(conn, &form)
}) })
.await??; .await??;
Ok(pm.into()) Ok(pm.into())

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