From: Dessalines Date: Thu, 27 Oct 2022 09:24:07 +0000 (-0400) Subject: Moving settings to Database. (#2492) X-Git-Url: http://these/git/%7B%60%24%7BwebArchiveUrl%7D/%22%7B%7D/readmes/%7B%7D/%7Bthis.state.url%7D?a=commitdiff_plain;h=235cc8b22897bfb3e71ba3dbd725d36863fea8ba;p=lemmy.git 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. --- diff --git a/Cargo.lock b/Cargo.lock index c3743cac..e726ae25 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2005,6 +2005,7 @@ dependencies = [ "lemmy_db_views_moderator", "lemmy_utils", "percent-encoding", + "regex", "reqwest", "reqwest-middleware", "rosetta-i18n", @@ -2096,6 +2097,7 @@ dependencies = [ "sha2", "strum", "strum_macros", + "typed-builder", "url", ] @@ -2110,6 +2112,7 @@ dependencies = [ "serial_test", "tracing", "typed-builder", + "url", ] [[package]] @@ -2227,6 +2230,7 @@ dependencies = [ "strum_macros", "tracing", "tracing-error", + "typed-builder", "url", "uuid 1.1.2", ] diff --git a/api_tests/.eslintrc.json b/api_tests/.eslintrc.json index aec9f66e..6759f712 100644 --- a/api_tests/.eslintrc.json +++ b/api_tests/.eslintrc.json @@ -4,11 +4,11 @@ "browser": true }, "plugins": [ - "jane" + "@typescript-eslint" ], "extends": [ - "plugin:jane/recommended", - "plugin:jane/typescript" + "eslint:recommended", + "plugin:@typescript-eslint/recommended" ], "parser": "@typescript-eslint/parser", "parserOptions": { diff --git a/api_tests/.prettierrc.js b/api_tests/.prettierrc.js index 5983e1a1..8d36af38 100644 --- a/api_tests/.prettierrc.js +++ b/api_tests/.prettierrc.js @@ -1,4 +1,4 @@ -module.exports = Object.assign(require('eslint-plugin-jane/prettier-ts'), { - arrowParens: 'avoid', +module.exports = Object.assign(require("eslint-plugin-prettier"), { + arrowParens: "avoid", semi: true, }); diff --git a/api_tests/package.json b/api_tests/package.json index 718f454c..48d87399 100644 --- a/api_tests/package.json +++ b/api_tests/package.json @@ -14,15 +14,17 @@ "devDependencies": { "@sniptt/monads": "^0.5.10", "@types/jest": "^26.0.23", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", "class-transformer": "^0.5.1", - "eslint": "^8.20.0", - "eslint-plugin-jane": "^11.2.2", + "eslint": "^8.25.0", + "eslint-plugin-prettier": "^4.0.0", "jest": "^27.0.6", - "lemmy-js-client": "0.17.0-rc.37", + "lemmy-js-client": "0.17.0-rc.47", "node-fetch": "^2.6.1", "prettier": "^2.7.1", "reflect-metadata": "^0.1.13", "ts-jest": "^27.0.3", - "typescript": "^4.6.4" + "typescript": "^4.8.4" } } diff --git a/api_tests/src/comment.spec.ts b/api_tests/src/comment.spec.ts index ca9cc3da..5fb6bc80 100644 --- a/api_tests/src/comment.spec.ts +++ b/api_tests/src/comment.spec.ts @@ -1,7 +1,7 @@ jest.setTimeout(180000); -import {None, Some} from '@sniptt/monads'; -import { CommentView } from 'lemmy-js-client'; -import { PostResponse } from 'lemmy-js-client'; +import { None, Some } from "@sniptt/monads"; +import { CommentView } from "lemmy-js-client"; +import { PostResponse } from "lemmy-js-client"; import { alpha, @@ -31,7 +31,7 @@ import { getComments, getCommentParentId, resolveCommunity, -} from './shared'; +} from "./shared"; let postRes: PostResponse; @@ -41,10 +41,7 @@ beforeAll(async () => { await followBeta(alpha); await followBeta(gamma); let betaCommunity = (await resolveBetaCommunity(alpha)).community; - postRes = await createPost( - alpha, - betaCommunity.unwrap().community.id - ); + postRes = await createPost(alpha, betaCommunity.unwrap().community.id); }); afterAll(async () => { @@ -65,7 +62,7 @@ function assertCommentFederation( expect(commentOne.comment.removed).toBe(commentOne.comment.removed); } -test('Create a comment', async () => { +test("Create a comment", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); expect(commentRes.comment_view.comment.content).toBeDefined(); expect(commentRes.comment_view.community.local).toBe(false); @@ -73,7 +70,9 @@ test('Create a comment', async () => { expect(commentRes.comment_view.counts.score).toBe(1); // Make sure that comment is liked on beta - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); expect(betaComment).toBeDefined(); expect(betaComment.community.local).toBe(true); expect(betaComment.creator.local).toBe(false); @@ -81,15 +80,17 @@ test('Create a comment', async () => { assertCommentFederation(betaComment, commentRes.comment_view); }); -test('Create a comment in a non-existent post', async () => { - let commentRes = await createComment(alpha, -1, None) as any; - expect(commentRes.error).toBe('couldnt_find_post'); +test("Create a comment in a non-existent post", async () => { + let commentRes = (await createComment(alpha, -1, None)) as any; + expect(commentRes.error).toBe("couldnt_find_post"); }); -test('Update a comment', async () => { +test("Update a comment", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); // Federate the comment first - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment; + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment; assertCommentFederation(betaComment.unwrap(), commentRes.comment_view); let updateCommentRes = await editComment( @@ -97,23 +98,19 @@ test('Update a comment', async () => { commentRes.comment_view.comment.id ); expect(updateCommentRes.comment_view.comment.content).toBe( - 'A jest test federated comment update' + "A jest test federated comment update" ); expect(updateCommentRes.comment_view.community.local).toBe(false); expect(updateCommentRes.comment_view.creator.local).toBe(true); // Make sure that post is updated on beta - let betaCommentUpdated = (await resolveComment( - beta, - commentRes.comment_view.comment - )).comment.unwrap(); - assertCommentFederation( - betaCommentUpdated, - updateCommentRes.comment_view - ); + let betaCommentUpdated = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); + assertCommentFederation(betaCommentUpdated, updateCommentRes.comment_view); }); -test('Delete a comment', async () => { +test("Delete a comment", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); let deleteCommentRes = await deleteComment( @@ -125,8 +122,11 @@ test('Delete a comment', async () => { expect(deleteCommentRes.comment_view.comment.content).toBe(""); // Make sure that comment is undefined on beta - let betaCommentRes = await resolveComment(beta, commentRes.comment_view.comment) as any; - expect(betaCommentRes.error).toBe('couldnt_find_object'); + let betaCommentRes = (await resolveComment( + beta, + commentRes.comment_view.comment + )) as any; + expect(betaCommentRes.error).toBe("couldnt_find_object"); let undeleteCommentRes = await deleteComment( alpha, @@ -136,15 +136,14 @@ test('Delete a comment', async () => { expect(undeleteCommentRes.comment_view.comment.deleted).toBe(false); // Make sure that comment is undeleted on beta - let betaComment2 = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment2 = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); expect(betaComment2.comment.deleted).toBe(false); - assertCommentFederation( - betaComment2, - undeleteCommentRes.comment_view - ); + assertCommentFederation(betaComment2, undeleteCommentRes.comment_view); }); -test('Remove a comment from admin and community on the same instance', async () => { +test("Remove a comment from admin and community on the same instance", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); // Get the id for beta @@ -158,14 +157,20 @@ test('Remove a comment from admin and community on the same instance', async () expect(removeCommentRes.comment_view.comment.content).toBe(""); // Make sure that comment is removed on alpha (it gets pushed since an admin from beta removed it) - let refetchedPostComments = await getComments(alpha, postRes.post_view.post.id); + let refetchedPostComments = await getComments( + alpha, + postRes.post_view.post.id + ); expect(refetchedPostComments.comments[0].comment.removed).toBe(true); let unremoveCommentRes = await removeComment(beta, false, betaCommentId); expect(unremoveCommentRes.comment_view.comment.removed).toBe(false); // Make sure that comment is unremoved on beta - let refetchedPostComments2 = await getComments(alpha, postRes.post_view.post.id); + let refetchedPostComments2 = await getComments( + alpha, + postRes.post_view.post.id + ); expect(refetchedPostComments2.comments[0].comment.removed).toBe(false); assertCommentFederation( refetchedPostComments2.comments[0], @@ -173,7 +178,7 @@ test('Remove a comment from admin and community on the same instance', async () ); }); -test('Remove a comment from admin and community on different instance', async () => { +test("Remove a comment from admin and community on different instance", async () => { let alpha_user = await registerUser(alpha); let newAlphaApi: API = { client: alpha.client, @@ -186,11 +191,17 @@ test('Remove a comment from admin and community on different instance', async () newAlphaApi, newCommunity.community_view.community.id ); - let commentRes = await createComment(newAlphaApi, newPost.post_view.post.id, None); + let commentRes = await createComment( + newAlphaApi, + newPost.post_view.post.id, + None + ); expect(commentRes.comment_view.comment.content).toBeDefined(); // Beta searches that to cache it, then removes it - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); let removeCommentRes = await removeComment( beta, true, @@ -199,29 +210,39 @@ test('Remove a comment from admin and community on different instance', async () expect(removeCommentRes.comment_view.comment.removed).toBe(true); // Make sure its not removed on alpha - let refetchedPostComments = await getComments(alpha, newPost.post_view.post.id); + let refetchedPostComments = await getComments( + alpha, + newPost.post_view.post.id + ); expect(refetchedPostComments.comments[0].comment.removed).toBe(false); - assertCommentFederation(refetchedPostComments.comments[0], commentRes.comment_view); + assertCommentFederation( + refetchedPostComments.comments[0], + commentRes.comment_view + ); }); -test('Unlike a comment', async () => { +test("Unlike a comment", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); let unlike = await likeComment(alpha, 0, commentRes.comment_view.comment); expect(unlike.comment_view.counts.score).toBe(0); // Make sure that post is unliked on beta - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); expect(betaComment).toBeDefined(); expect(betaComment.community.local).toBe(true); expect(betaComment.creator.local).toBe(false); expect(betaComment.counts.score).toBe(0); }); -test('Federated comment like', async () => { +test("Federated comment like", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); // Find the comment on beta - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); let like = await likeComment(beta, 1, betaComment.comment); expect(like.comment_view.counts.score).toBe(2); @@ -231,10 +252,12 @@ test('Federated comment like', async () => { expect(postComments.comments[0].counts.score).toBe(2); }); -test('Reply to a comment', async () => { +test("Reply to a comment", async () => { // Create a comment on alpha, find it on beta let commentRes = await createComment(alpha, postRes.post_view.post.id, None); - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); // find that comment id on beta @@ -247,7 +270,9 @@ test('Reply to a comment', async () => { expect(replyRes.comment_view.comment.content).toBeDefined(); expect(replyRes.comment_view.community.local).toBe(true); expect(replyRes.comment_view.creator.local).toBe(true); - expect(getCommentParentId(replyRes.comment_view.comment).unwrap()).toBe(betaComment.comment.id); + expect(getCommentParentId(replyRes.comment_view.comment).unwrap()).toBe( + betaComment.comment.id + ); expect(replyRes.comment_view.counts.score).toBe(1); // Make sure that comment is seen on alpha @@ -257,16 +282,18 @@ test('Reply to a comment', async () => { let postComments = await getComments(alpha, postRes.post_view.post.id); let alphaComment = postComments.comments[0]; expect(alphaComment.comment.content).toBeDefined(); - expect(getCommentParentId(alphaComment.comment).unwrap()).toBe(postComments.comments[1].comment.id); + expect(getCommentParentId(alphaComment.comment).unwrap()).toBe( + postComments.comments[1].comment.id + ); expect(alphaComment.community.local).toBe(false); expect(alphaComment.creator.local).toBe(false); expect(alphaComment.counts.score).toBe(1); assertCommentFederation(alphaComment, replyRes.comment_view); }); -test('Mention beta', async () => { +test("Mention beta", async () => { // Create a mention on alpha - let mentionContent = 'A test mention of @lemmy_beta@lemmy-beta:8551'; + let mentionContent = "A test mention of @lemmy_beta@lemmy-beta:8551"; let commentRes = await createComment(alpha, postRes.post_view.post.id, None); let mentionRes = await createComment( alpha, @@ -286,23 +313,29 @@ test('Mention beta', async () => { expect(mentionsRes.mentions[0].counts.score).toBe(1); }); -test('Comment Search', async () => { +test("Comment Search", async () => { let commentRes = await createComment(alpha, postRes.post_view.post.id, None); - let betaComment = (await resolveComment(beta, commentRes.comment_view.comment)).comment.unwrap(); + let betaComment = ( + await resolveComment(beta, commentRes.comment_view.comment) + ).comment.unwrap(); assertCommentFederation(betaComment, commentRes.comment_view); }); -test('A and G subscribe to B (center) A posts, G mentions B, it gets announced to A', async () => { +test("A and G subscribe to B (center) A posts, G mentions B, it gets announced to A", async () => { // Create a local post - let alphaCommunity = (await resolveCommunity(alpha, "!main@lemmy-alpha:8541")).community.unwrap(); + let alphaCommunity = ( + await resolveCommunity(alpha, "!main@lemmy-alpha:8541") + ).community.unwrap(); let alphaPost = await createPost(alpha, alphaCommunity.community.id); expect(alphaPost.post_view.community.local).toBe(true); // Make sure gamma sees it - let gammaPost = (await resolvePost(gamma, alphaPost.post_view.post)).post.unwrap(); + let gammaPost = ( + await resolvePost(gamma, alphaPost.post_view.post) + ).post.unwrap(); let commentContent = - 'A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551'; + "A jest test federated comment announce, lets mention @lemmy_beta@lemmy-beta:8551"; let commentRes = await createComment( gamma, gammaPost.post.id, @@ -315,12 +348,18 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t expect(commentRes.comment_view.counts.score).toBe(1); // Make sure alpha sees it - let alphaPostComments2 = await getComments(alpha, alphaPost.post_view.post.id); + let alphaPostComments2 = await getComments( + alpha, + alphaPost.post_view.post.id + ); expect(alphaPostComments2.comments[0].comment.content).toBe(commentContent); expect(alphaPostComments2.comments[0].community.local).toBe(true); expect(alphaPostComments2.comments[0].creator.local).toBe(false); expect(alphaPostComments2.comments[0].counts.score).toBe(1); - assertCommentFederation(alphaPostComments2.comments[0], commentRes.comment_view); + assertCommentFederation( + alphaPostComments2.comments[0], + commentRes.comment_view + ); // Make sure beta has mentions let mentionsRes = await getMentions(beta); @@ -331,28 +370,32 @@ test('A and G subscribe to B (center) A posts, G mentions B, it gets announced t // expect(mentionsRes.mentions[0].score).toBe(1); }); -test('Check that activity from another instance is sent to third instance', async () => { +test("Check that activity from another instance is sent to third instance", async () => { // Alpha and gamma users follow beta community let alphaFollow = await followBeta(alpha); expect(alphaFollow.community_view.community.local).toBe(false); - expect(alphaFollow.community_view.community.name).toBe('main'); + expect(alphaFollow.community_view.community.name).toBe("main"); let gammaFollow = await followBeta(gamma); expect(gammaFollow.community_view.community.local).toBe(false); - expect(gammaFollow.community_view.community.name).toBe('main'); + expect(gammaFollow.community_view.community.name).toBe("main"); // Create a post on beta let betaPost = await createPost(beta, 2); expect(betaPost.post_view.community.local).toBe(true); // Make sure gamma and alpha see it - let gammaPost = (await resolvePost(gamma, betaPost.post_view.post)).post.unwrap(); + let gammaPost = ( + await resolvePost(gamma, betaPost.post_view.post) + ).post.unwrap(); expect(gammaPost.post).toBeDefined(); - let alphaPost = (await resolvePost(alpha, betaPost.post_view.post)).post.unwrap(); + let alphaPost = ( + await resolvePost(alpha, betaPost.post_view.post) + ).post.unwrap(); expect(alphaPost.post).toBeDefined(); // The bug: gamma comments, and alpha should see it. - let commentContent = 'Comment from gamma'; + let commentContent = "Comment from gamma"; let commentRes = await createComment( gamma, gammaPost.post.id, @@ -370,13 +413,16 @@ test('Check that activity from another instance is sent to third instance', asyn expect(alphaPostComments2.comments[0].community.local).toBe(false); expect(alphaPostComments2.comments[0].creator.local).toBe(false); expect(alphaPostComments2.comments[0].counts.score).toBe(1); - assertCommentFederation(alphaPostComments2.comments[0], commentRes.comment_view); + assertCommentFederation( + alphaPostComments2.comments[0], + commentRes.comment_view + ); await unfollowRemotes(alpha); await unfollowRemotes(gamma); }); -test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.', async () => { +test("Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedded comments, A subs to B, B updates the lowest level comment, A fetches both the post and all the inreplyto comments for that post.", async () => { // Unfollow all remote communities let site = await unfollowRemotes(alpha); expect( @@ -387,7 +433,7 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde let postRes = await createPost(beta, 2); expect(postRes.post_view.post.name).toBeDefined(); - let parentCommentContent = 'An invisible top level comment from beta'; + let parentCommentContent = "An invisible top level comment from beta"; let parentCommentRes = await createComment( beta, postRes.post_view.post.id, @@ -399,7 +445,7 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde ); // B creates a comment, then a child one of that. - let childCommentContent = 'An invisible child comment from beta'; + let childCommentContent = "An invisible child comment from beta"; let childCommentRes = await createComment( beta, postRes.post_view.post.id, @@ -413,50 +459,62 @@ test('Fetch in_reply_tos: A is unsubbed from B, B makes a post, and some embedde // Follow beta again let follow = await followBeta(alpha); expect(follow.community_view.community.local).toBe(false); - expect(follow.community_view.community.name).toBe('main'); + expect(follow.community_view.community.name).toBe("main"); // An update to the child comment on beta, should push the post, parent, and child to alpha now - let updatedCommentContent = 'An update child comment from beta'; + let updatedCommentContent = Some("An update child comment from beta"); let updateRes = await editComment( beta, childCommentRes.comment_view.comment.id, updatedCommentContent ); - expect(updateRes.comment_view.comment.content).toBe(updatedCommentContent); + expect(updateRes.comment_view.comment.content).toBe( + updatedCommentContent.unwrap() + ); // Get the post from alpha - let alphaPostB = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap(); + let alphaPostB = ( + await resolvePost(alpha, postRes.post_view.post) + ).post.unwrap(); let alphaPost = await getPost(alpha, alphaPostB.post.id); let alphaPostComments = await getComments(alpha, alphaPostB.post.id); expect(alphaPost.post_view.post.name).toBeDefined(); - assertCommentFederation(alphaPostComments.comments[1], parentCommentRes.comment_view); - assertCommentFederation(alphaPostComments.comments[0], updateRes.comment_view); + assertCommentFederation( + alphaPostComments.comments[1], + parentCommentRes.comment_view + ); + assertCommentFederation( + alphaPostComments.comments[0], + updateRes.comment_view + ); expect(alphaPost.post_view.community.local).toBe(false); expect(alphaPost.post_view.creator.local).toBe(false); await unfollowRemotes(alpha); }); - -test('Report a comment', async () => { +test("Report a comment", async () => { let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap(); - let postRes = (await createPost(beta, betaCommunity.community.id)).post_view.post; + let postRes = (await createPost(beta, betaCommunity.community.id)).post_view + .post; expect(postRes).toBeDefined(); - let commentRes = (await createComment(beta, postRes.id, None)).comment_view.comment; + let commentRes = (await createComment(beta, postRes.id, None)).comment_view + .comment; expect(commentRes).toBeDefined(); - let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap().comment; - let alphaReport = (await reportComment(alpha, alphaComment.id, randomString(10))) - .comment_report_view.comment_report; + let alphaComment = (await resolveComment(alpha, commentRes)).comment.unwrap() + .comment; + let alphaReport = ( + await reportComment(alpha, alphaComment.id, randomString(10)) + ).comment_report_view.comment_report; - let betaReport = (await listCommentReports(beta)).comment_reports[0].comment_report; + let betaReport = (await listCommentReports(beta)).comment_reports[0] + .comment_report; expect(betaReport).toBeDefined(); expect(betaReport.resolved).toBe(false); - expect(betaReport.original_comment_text).toBe(alphaReport.original_comment_text); + expect(betaReport.original_comment_text).toBe( + alphaReport.original_comment_text + ); expect(betaReport.reason).toBe(alphaReport.reason); }); -function N(gamma: API, id: number, N: any, commentContent: string) { - throw new Error('Function not implemented.'); -} - diff --git a/api_tests/src/community.spec.ts b/api_tests/src/community.spec.ts index 381b546b..0ed50ec4 100644 --- a/api_tests/src/community.spec.ts +++ b/api_tests/src/community.spec.ts @@ -1,5 +1,5 @@ jest.setTimeout(120000); -import { CommunityView } from 'lemmy-js-client'; +import { CommunityView } from "lemmy-js-client"; import { alpha, @@ -18,7 +18,7 @@ import { createPost, getPost, resolvePost, -} from './shared'; +} from "./shared"; beforeAll(async () => { await setupLogins(); @@ -34,8 +34,12 @@ function assertCommunityFederation( expect(communityOne.community.description.unwrapOr("none")).toBe( communityTwo.community.description.unwrapOr("none") ); - expect(communityOne.community.icon.unwrapOr("none")).toBe(communityTwo.community.icon.unwrapOr("none")); - expect(communityOne.community.banner.unwrapOr("none")).toBe(communityTwo.community.banner.unwrapOr("none")); + expect(communityOne.community.icon.unwrapOr("none")).toBe( + communityTwo.community.icon.unwrapOr("none") + ); + expect(communityOne.community.banner.unwrapOr("none")).toBe( + communityTwo.community.banner.unwrapOr("none") + ); expect(communityOne.community.published).toBe( communityTwo.community.published ); @@ -44,35 +48,35 @@ function assertCommunityFederation( expect(communityOne.community.deleted).toBe(communityTwo.community.deleted); } -test('Create community', async () => { +test("Create community", async () => { let communityRes = await createCommunity(alpha); expect(communityRes.community_view.community.name).toBeDefined(); // A dupe check let prevName = communityRes.community_view.community.name; let communityRes2: any = await createCommunity(alpha, prevName); - expect(communityRes2['error']).toBe('community_already_exists'); + expect(communityRes2["error"]).toBe("community_already_exists"); // Cache the community on beta, make sure it has the other fields let searchShort = `!${prevName}@lemmy-alpha:8541`; - let betaCommunity = (await resolveCommunity(beta, searchShort)).community.unwrap(); + let betaCommunity = ( + await resolveCommunity(beta, searchShort) + ).community.unwrap(); assertCommunityFederation(betaCommunity, communityRes.community_view); }); -test('Delete community', async () => { +test("Delete community", async () => { let communityRes = await createCommunity(beta); // Cache the community on Alpha let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; - let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap(); + let alphaCommunity = ( + await resolveCommunity(alpha, searchShort) + ).community.unwrap(); assertCommunityFederation(alphaCommunity, communityRes.community_view); // Follow the community from alpha - let follow = await followCommunity( - alpha, - true, - alphaCommunity.community.id - ); + let follow = await followCommunity(alpha, true, alphaCommunity.community.id); // Make sure the follow response went through expect(follow.community_view.community.local).toBe(false); @@ -83,7 +87,9 @@ test('Delete community', async () => { communityRes.community_view.community.id ); expect(deleteCommunityRes.community_view.community.deleted).toBe(true); - expect(deleteCommunityRes.community_view.community.title).toBe(communityRes.community_view.community.title); + expect(deleteCommunityRes.community_view.community.title).toBe( + communityRes.community_view.community.title + ); // Make sure it got deleted on A let communityOnAlphaDeleted = await getCommunity( @@ -110,20 +116,18 @@ test('Delete community', async () => { ); }); -test('Remove community', async () => { +test("Remove community", async () => { let communityRes = await createCommunity(beta); // Cache the community on Alpha let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; - let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap(); + let alphaCommunity = ( + await resolveCommunity(alpha, searchShort) + ).community.unwrap(); assertCommunityFederation(alphaCommunity, communityRes.community_view); // Follow the community from alpha - let follow = await followCommunity( - alpha, - true, - alphaCommunity.community.id - ); + let follow = await followCommunity(alpha, true, alphaCommunity.community.id); // Make sure the follow response went through expect(follow.community_view.community.local).toBe(false); @@ -134,7 +138,9 @@ test('Remove community', async () => { communityRes.community_view.community.id ); expect(removeCommunityRes.community_view.community.removed).toBe(true); - expect(removeCommunityRes.community_view.community.title).toBe(communityRes.community_view.community.title); + expect(removeCommunityRes.community_view.community.title).toBe( + communityRes.community_view.community.title + ); // Make sure it got Removed on A let communityOnAlphaRemoved = await getCommunity( @@ -161,34 +167,53 @@ test('Remove community', async () => { ); }); -test('Search for beta community', async () => { +test("Search for beta community", async () => { let communityRes = await createCommunity(beta); expect(communityRes.community_view.community.name).toBeDefined(); let searchShort = `!${communityRes.community_view.community.name}@lemmy-beta:8551`; - let alphaCommunity = (await resolveCommunity(alpha, searchShort)).community.unwrap(); + let alphaCommunity = ( + await resolveCommunity(alpha, searchShort) + ).community.unwrap(); assertCommunityFederation(alphaCommunity, communityRes.community_view); }); -test('Admin actions in remote community are not federated to origin', async () => { +test("Admin actions in remote community are not federated to origin", async () => { // create a community on alpha let communityRes = (await createCommunity(alpha)).community_view; expect(communityRes.community.name).toBeDefined(); // gamma follows community and posts in it - let gammaCommunity = (await resolveCommunity(gamma, communityRes.community.actor_id)).community.unwrap(); - let gammaFollow = (await followCommunity(gamma, true, gammaCommunity.community.id)); + let gammaCommunity = ( + await resolveCommunity(gamma, communityRes.community.actor_id) + ).community.unwrap(); + let gammaFollow = await followCommunity( + gamma, + true, + gammaCommunity.community.id + ); expect(gammaFollow.community_view.subscribed).toBe("Subscribed"); - let gammaPost = (await createPost(gamma, gammaCommunity.community.id)).post_view; + let gammaPost = (await createPost(gamma, gammaCommunity.community.id)) + .post_view; expect(gammaPost.post.id).toBeDefined(); expect(gammaPost.creator_banned_from_community).toBe(false); // admin of beta decides to ban gamma from community - let betaCommunity = (await resolveCommunity(beta, communityRes.community.actor_id)).community.unwrap(); - let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view.person; - let bannedUserInfo2 = (await resolvePerson(beta, bannedUserInfo1.actor_id)).person.unwrap(); - let banRes = (await banPersonFromCommunity(beta, bannedUserInfo2.person.id, betaCommunity.community.id, true, true)); - console.log(banRes); + let betaCommunity = ( + await resolveCommunity(beta, communityRes.community.actor_id) + ).community.unwrap(); + let bannedUserInfo1 = (await getSite(gamma)).my_user.unwrap().local_user_view + .person; + let bannedUserInfo2 = ( + await resolvePerson(beta, bannedUserInfo1.actor_id) + ).person.unwrap(); + let banRes = await banPersonFromCommunity( + beta, + bannedUserInfo2.person.id, + betaCommunity.community.id, + true, + true + ); expect(banRes.banned).toBe(true); // ban doesnt federate to community's origin instance alpha @@ -196,6 +221,6 @@ test('Admin actions in remote community are not federated to origin', async () = expect(alphaPost.creator_banned_from_community).toBe(false); // and neither to gamma - let gammaPost2 = (await getPost(gamma, gammaPost.post.id)); + let gammaPost2 = await getPost(gamma, gammaPost.post.id); expect(gammaPost2.post_view.creator_banned_from_community).toBe(false); }); diff --git a/api_tests/src/follow.spec.ts b/api_tests/src/follow.spec.ts index 54fc1666..f80b40de 100644 --- a/api_tests/src/follow.spec.ts +++ b/api_tests/src/follow.spec.ts @@ -1,5 +1,6 @@ jest.setTimeout(120000); -import {SubscribedType} from 'lemmy-js-client'; +import { SubscribedType } from "lemmy-js-client"; + import { alpha, setupLogins, @@ -7,8 +8,7 @@ import { followCommunity, unfollowRemotes, getSite, - delay, -} from './shared'; +} from "./shared"; beforeAll(async () => { await setupLogins(); @@ -18,24 +18,20 @@ afterAll(async () => { await unfollowRemotes(alpha); }); -test('Follow federated community', async () => { +test("Follow federated community", async () => { let betaCommunity = (await resolveBetaCommunity(alpha)).community.unwrap(); - let follow = await followCommunity( - alpha, - true, - betaCommunity.community.id - ); + let follow = await followCommunity(alpha, true, betaCommunity.community.id); // Make sure the follow response went through expect(follow.community_view.community.local).toBe(false); - expect(follow.community_view.community.name).toBe('main'); + expect(follow.community_view.community.name).toBe("main"); expect(follow.community_view.subscribed).toBe(SubscribedType.Subscribed); // Check it from local let site = await getSite(alpha); - let remoteCommunityId = site.my_user.unwrap().follows.find( - c => c.community.local == false - ).community.id; + let remoteCommunityId = site.my_user + .unwrap() + .follows.find(c => c.community.local == false).community.id; expect(remoteCommunityId).toBeDefined(); expect(site.my_user.unwrap().follows.length).toBe(2); diff --git a/api_tests/src/post.spec.ts b/api_tests/src/post.spec.ts index edf85ebd..d0e42c72 100644 --- a/api_tests/src/post.spec.ts +++ b/api_tests/src/post.spec.ts @@ -1,6 +1,7 @@ jest.setTimeout(120000); -import {None} from '@sniptt/monads'; -import { PostView, CommunityView } from 'lemmy-js-client'; +import { None } from "@sniptt/monads"; +import { PostView, CommunityView } from "lemmy-js-client"; + import { alpha, beta, @@ -33,8 +34,8 @@ import { API, getSite, unfollows, - resolveCommunity -} from './shared'; + resolveCommunity, +} from "./shared"; let betaCommunity: CommunityView; @@ -52,12 +53,22 @@ afterAll(async () => { function assertPostFederation(postOne: PostView, postTwo: PostView) { expect(postOne.post.ap_id).toBe(postTwo.post.ap_id); expect(postOne.post.name).toBe(postTwo.post.name); - expect(postOne.post.body.unwrapOr("none")).toBe(postTwo.post.body.unwrapOr("none")); - expect(postOne.post.url.unwrapOr("none")).toBe(postTwo.post.url.unwrapOr("none")); + expect(postOne.post.body.unwrapOr("none")).toBe( + postTwo.post.body.unwrapOr("none") + ); + expect(postOne.post.url.unwrapOr("https://google.com/")).toBe( + postTwo.post.url.unwrapOr("https://google.com/") + ); expect(postOne.post.nsfw).toBe(postTwo.post.nsfw); - expect(postOne.post.embed_title.unwrapOr("none")).toBe(postTwo.post.embed_title.unwrapOr("none")); - expect(postOne.post.embed_description.unwrapOr("none")).toBe(postTwo.post.embed_description.unwrapOr("none")); - expect(postOne.post.embed_html.unwrapOr("none")).toBe(postTwo.post.embed_html.unwrapOr("none")); + expect(postOne.post.embed_title.unwrapOr("none")).toBe( + postTwo.post.embed_title.unwrapOr("none") + ); + expect(postOne.post.embed_description.unwrapOr("none")).toBe( + postTwo.post.embed_description.unwrapOr("none") + ); + expect(postOne.post.embed_video_url.unwrapOr("none")).toBe( + postTwo.post.embed_video_url.unwrapOr("none") + ); expect(postOne.post.published).toBe(postTwo.post.published); expect(postOne.community.actor_id).toBe(postTwo.community.actor_id); expect(postOne.post.locked).toBe(postTwo.post.locked); @@ -65,7 +76,7 @@ function assertPostFederation(postOne: PostView, postTwo: PostView) { expect(postOne.post.deleted).toBe(postTwo.post.deleted); } -test('Create a post', async () => { +test("Create a post", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); expect(postRes.post_view.community.local).toBe(false); @@ -73,7 +84,9 @@ test('Create a post', async () => { expect(postRes.post_view.counts.score).toBe(1); // Make sure that post is liked on beta - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost).toBeDefined(); expect(betaPost.community.local).toBe(true); @@ -83,19 +96,19 @@ test('Create a post', async () => { // Delta only follows beta, so it should not see an alpha ap_id let deltaPost = (await resolvePost(delta, postRes.post_view.post)).post; - expect(deltaPost.isNone()).toBe(true) + expect(deltaPost.isNone()).toBe(true); // Epsilon has alpha blocked, it should not see the alpha post let epsilonPost = (await resolvePost(epsilon, postRes.post_view.post)).post; expect(epsilonPost.isNone()).toBe(true); }); -test('Create a post in a non-existent community', async () => { - let postRes = await createPost(alpha, -2) as any; - expect(postRes.error).toBe('couldnt_find_community'); +test("Create a post in a non-existent community", async () => { + let postRes = (await createPost(alpha, -2)) as any; + expect(postRes.error).toBe("couldnt_find_community"); }); -test('Unlike a post', async () => { +test("Unlike a post", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); let unlike = await likePost(alpha, 0, postRes.post_view.post); expect(unlike.post_view.counts.score).toBe(0); @@ -105,7 +118,9 @@ test('Unlike a post', async () => { expect(unlike2.post_view.counts.score).toBe(0); // Make sure that post is unliked on beta - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost).toBeDefined(); expect(betaPost.community.local).toBe(true); expect(betaPost.creator.local).toBe(false); @@ -113,36 +128,42 @@ test('Unlike a post', async () => { assertPostFederation(betaPost, postRes.post_view); }); -test('Update a post', async () => { +test("Update a post", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); - let updatedName = 'A jest test federated post, updated'; + let updatedName = "A jest test federated post, updated"; let updatedPost = await editPost(alpha, postRes.post_view.post); expect(updatedPost.post_view.post.name).toBe(updatedName); expect(updatedPost.post_view.community.local).toBe(false); expect(updatedPost.post_view.creator.local).toBe(true); // Make sure that post is updated on beta - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost.community.local).toBe(true); expect(betaPost.creator.local).toBe(false); expect(betaPost.post.name).toBe(updatedName); assertPostFederation(betaPost, updatedPost.post_view); // Make sure lemmy beta cannot update the post - let updatedPostBeta = await editPost(beta, betaPost.post) as any; - expect(updatedPostBeta.error).toBe('no_post_edit_allowed'); + let updatedPostBeta = (await editPost(beta, betaPost.post)) as any; + expect(updatedPostBeta.error).toBe("no_post_edit_allowed"); }); -test('Sticky a post', async () => { +test("Sticky a post", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); - let betaPost1 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost1 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); let stickiedPostRes = await stickyPost(beta, true, betaPost1.post); expect(stickiedPostRes.post_view.post.stickied).toBe(true); // Make sure that post is stickied on beta - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost.community.local).toBe(true); expect(betaPost.creator.local).toBe(false); expect(betaPost.post.stickied).toBe(true); @@ -152,25 +173,33 @@ test('Sticky a post', async () => { expect(unstickiedPost.post_view.post.stickied).toBe(false); // Make sure that post is unstickied on beta - let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost2 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost2.community.local).toBe(true); expect(betaPost2.creator.local).toBe(false); expect(betaPost2.post.stickied).toBe(false); // Make sure that gamma cannot sticky the post on beta - let gammaPost = (await resolvePost(gamma, postRes.post_view.post)).post.unwrap(); + let gammaPost = ( + await resolvePost(gamma, postRes.post_view.post) + ).post.unwrap(); let gammaTrySticky = await stickyPost(gamma, true, gammaPost.post); - let betaPost3 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost3 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(gammaTrySticky.post_view.post.stickied).toBe(true); expect(betaPost3.post.stickied).toBe(false); }); -test('Lock a post', async () => { +test("Lock a post", async () => { await followCommunity(alpha, true, betaCommunity.community.id); let postRes = await createPost(alpha, betaCommunity.community.id); // Lock the post - let betaPost1 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost1 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); let lockedPostRes = await lockPost(beta, true, betaPost1.post); expect(lockedPostRes.post_view.post.locked).toBe(true); @@ -181,7 +210,7 @@ test('Lock a post', async () => { // Try to make a new comment there, on alpha let comment: any = await createComment(alpha, alphaPost1.post.id, None); - expect(comment['error']).toBe('locked'); + expect(comment["error"]).toBe("locked"); // Unlock a post let unlockedPost = await lockPost(beta, false, betaPost1.post); @@ -199,7 +228,7 @@ test('Lock a post', async () => { expect(commentAlpha).toBeDefined(); }); -test('Delete a post', async () => { +test("Delete a post", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); @@ -217,26 +246,38 @@ test('Delete a post', async () => { expect(undeletedPost.post_view.post.deleted).toBe(false); // Make sure lemmy beta sees post is undeleted - let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost2 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost2.post.deleted).toBe(false); assertPostFederation(betaPost2, undeletedPost.post_view); // Make sure lemmy beta cannot delete the post - let deletedPostBeta = await deletePost(beta, true, betaPost2.post) as any; - expect(deletedPostBeta.error).toStrictEqual('no_post_edit_allowed'); + let deletedPostBeta = (await deletePost(beta, true, betaPost2.post)) as any; + expect(deletedPostBeta.error).toStrictEqual("no_post_edit_allowed"); }); -test('Remove a post from admin and community on different instance', async () => { - let gammaCommunity = await resolveCommunity(gamma, betaCommunity.community.actor_id); - let postRes = await createPost(gamma, gammaCommunity.community.unwrap().community.id); +test("Remove a post from admin and community on different instance", async () => { + let gammaCommunity = await resolveCommunity( + gamma, + betaCommunity.community.actor_id + ); + let postRes = await createPost( + gamma, + gammaCommunity.community.unwrap().community.id + ); - let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap(); + let alphaPost = ( + await resolvePost(alpha, postRes.post_view.post) + ).post.unwrap(); let removedPost = await removePost(alpha, true, alphaPost.post); expect(removedPost.post_view.post.removed).toBe(true); expect(removedPost.post_view.post.name).toBe(postRes.post_view.post.name); // Make sure lemmy beta sees post is NOT removed - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost.post.removed).toBe(false); // Undelete @@ -244,12 +285,14 @@ test('Remove a post from admin and community on different instance', async () => expect(undeletedPost.post_view.post.removed).toBe(false); // Make sure lemmy beta sees post is undeleted - let betaPost2 = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost2 = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost2.post.removed).toBe(false); assertPostFederation(betaPost2, undeletedPost.post_view); }); -test('Remove a post from admin and community on same instance', async () => { +test("Remove a post from admin and community on same instance", async () => { await followBeta(alpha); let postRes = await createPost(alpha, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); @@ -279,26 +322,31 @@ test('Remove a post from admin and community on same instance', async () => { await unfollowRemotes(alpha); }); -test('Search for a post', async () => { +test("Search for a post", async () => { await unfollowRemotes(alpha); let postRes = await createPost(alpha, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); - let betaPost = (await resolvePost(beta, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(beta, postRes.post_view.post) + ).post.unwrap(); expect(betaPost.post.name).toBeDefined(); }); -test('Enforce site ban for federated user', async () => { +test("Enforce site ban for federated user", async () => { // create a test user let alphaUserJwt = await registerUser(alpha); expect(alphaUserJwt).toBeDefined(); let alpha_user: API = { - client: alpha.client, - auth: alphaUserJwt.jwt, + client: alpha.client, + auth: alphaUserJwt.jwt, }; - let alphaUserActorId = (await getSite(alpha_user)).my_user.unwrap().local_user_view.person.actor_id; + let alphaUserActorId = (await getSite(alpha_user)).my_user.unwrap() + .local_user_view.person.actor_id; expect(alphaUserActorId).toBeDefined(); - let alphaPerson = (await resolvePerson(alpha_user, alphaUserActorId)).person.unwrap(); + let alphaPerson = ( + await resolvePerson(alpha_user, alphaUserActorId) + ).person.unwrap(); expect(alphaPerson).toBeDefined(); // alpha makes post in beta community, it federates to beta instance @@ -307,7 +355,12 @@ test('Enforce site ban for federated user', async () => { expect(searchBeta1.posts[0]).toBeDefined(); // ban alpha from its instance - let banAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, true, true); + let banAlpha = await banPersonFromSite( + alpha, + alphaPerson.person.id, + true, + true + ); expect(banAlpha.banned).toBe(true); // alpha ban should be federated to beta @@ -319,7 +372,12 @@ test('Enforce site ban for federated user', async () => { expect(searchBeta2.posts[0]).toBeUndefined(); // Unban alpha - let unBanAlpha = await banPersonFromSite(alpha, alphaPerson.person.id, false, false); + let unBanAlpha = await banPersonFromSite( + alpha, + alphaPerson.person.id, + false, + false + ); expect(unBanAlpha.banned).toBe(false); // alpha makes new post in beta community, it federates @@ -327,11 +385,11 @@ test('Enforce site ban for federated user', async () => { let searchBeta3 = await searchPostLocal(beta, postRes2.post_view.post); expect(searchBeta3.posts[0]).toBeDefined(); - let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId) + let alphaUserOnBeta2 = await resolvePerson(beta, alphaUserActorId); expect(alphaUserOnBeta2.person.unwrap().person.banned).toBe(false); }); -test('Enforce community ban for federated user', async () => { +test("Enforce community ban for federated user", async () => { let alphaShortname = `@lemmy_alpha@lemmy-alpha:8541`; let alphaPerson = (await resolvePerson(beta, alphaShortname)).person.unwrap(); expect(alphaPerson).toBeDefined(); @@ -342,7 +400,13 @@ test('Enforce community ban for federated user', async () => { expect(searchBeta1.posts[0]).toBeDefined(); // ban alpha from beta community - let banAlpha = await banPersonFromCommunity(beta, alphaPerson.person.id, 2, true, true); + let banAlpha = await banPersonFromCommunity( + beta, + alphaPerson.person.id, + 2, + true, + true + ); expect(banAlpha.banned).toBe(true); // ensure that the post by alpha got removed @@ -373,29 +437,37 @@ test('Enforce community ban for federated user', async () => { expect(searchBeta2.posts[0]).toBeDefined(); }); - -test('A and G subscribe to B (center) A posts, it gets announced to G', async () => { +test("A and G subscribe to B (center) A posts, it gets announced to G", async () => { let postRes = await createPost(alpha, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); - let betaPost = (await resolvePost(gamma, postRes.post_view.post)).post.unwrap(); + let betaPost = ( + await resolvePost(gamma, postRes.post_view.post) + ).post.unwrap(); expect(betaPost.post.name).toBeDefined(); }); -test('Report a post', async () => { +test("Report a post", async () => { let betaCommunity = (await resolveBetaCommunity(beta)).community.unwrap(); let postRes = await createPost(beta, betaCommunity.community.id); expect(postRes.post_view.post).toBeDefined(); - let alphaPost = (await resolvePost(alpha, postRes.post_view.post)).post.unwrap(); - let alphaReport = (await reportPost(alpha, alphaPost.post.id, randomString(10))) - .post_report_view.post_report; + let alphaPost = ( + await resolvePost(alpha, postRes.post_view.post) + ).post.unwrap(); + let alphaReport = ( + await reportPost(alpha, alphaPost.post.id, randomString(10)) + ).post_report_view.post_report; let betaReport = (await listPostReports(beta)).post_reports[0].post_report; expect(betaReport).toBeDefined(); expect(betaReport.resolved).toBe(false); expect(betaReport.original_post_name).toBe(alphaReport.original_post_name); - expect(betaReport.original_post_url.unwrapOr("none")).toBe(alphaReport.original_post_url.unwrapOr("none")); - expect(betaReport.original_post_body.unwrapOr("none")).toBe(alphaReport.original_post_body.unwrapOr("none")); + expect(betaReport.original_post_url.unwrapOr("none")).toBe( + alphaReport.original_post_url.unwrapOr("none") + ); + expect(betaReport.original_post_body.unwrapOr("none")).toBe( + alphaReport.original_post_body.unwrapOr("none") + ); expect(betaReport.reason).toBe(alphaReport.reason); }); diff --git a/api_tests/src/private_message.spec.ts b/api_tests/src/private_message.spec.ts index 0051f2c7..751f7223 100644 --- a/api_tests/src/private_message.spec.ts +++ b/api_tests/src/private_message.spec.ts @@ -9,7 +9,7 @@ import { listPrivateMessages, deletePrivateMessage, unfollowRemotes, -} from './shared'; +} from "./shared"; let recipient_id: number; @@ -23,7 +23,7 @@ afterAll(async () => { await unfollowRemotes(alpha); }); -test('Create a private message', async () => { +test("Create a private message", async () => { let pmRes = await createPrivateMessage(alpha, recipient_id); expect(pmRes.private_message_view.private_message.content).toBeDefined(); expect(pmRes.private_message_view.private_message.local).toBe(true); @@ -37,8 +37,8 @@ test('Create a private message', async () => { expect(betaPms.private_messages[0].recipient.local).toBe(true); }); -test('Update a private message', async () => { - let updatedContent = 'A jest test federated private message edited'; +test("Update a private message", async () => { + let updatedContent = "A jest test federated private message edited"; let pmRes = await createPrivateMessage(alpha, recipient_id); let pmUpdated = await editPrivateMessage( @@ -55,7 +55,7 @@ test('Update a private message', async () => { ); }); -test('Delete a private message', async () => { +test("Delete a private message", async () => { let pmRes = await createPrivateMessage(alpha, recipient_id); let betaPms1 = await listPrivateMessages(beta); let deletedPmRes = await deletePrivateMessage( diff --git a/api_tests/src/shared.ts b/api_tests/src/shared.ts index 7286ea5a..9bf23206 100644 --- a/api_tests/src/shared.ts +++ b/api_tests/src/shared.ts @@ -1,4 +1,4 @@ -import {None, Some, Option} from '@sniptt/monads'; +import { None, Some, Option } from "@sniptt/monads"; import { Login, LoginResponse, @@ -63,8 +63,8 @@ import { EditSite, CommentSortType, GetComments, - GetCommentsResponse -} from 'lemmy-js-client'; + GetCommentsResponse, +} from "lemmy-js-client"; export interface API { client: LemmyHttp; @@ -72,59 +72,59 @@ export interface API { } export let alpha: API = { - client: new LemmyHttp('http://127.0.0.1:8541'), + client: new LemmyHttp("http://127.0.0.1:8541"), auth: None, }; export let beta: API = { - client: new LemmyHttp('http://127.0.0.1:8551'), + client: new LemmyHttp("http://127.0.0.1:8551"), auth: None, }; export let gamma: API = { - client: new LemmyHttp('http://127.0.0.1:8561'), + client: new LemmyHttp("http://127.0.0.1:8561"), auth: None, }; export let delta: API = { - client: new LemmyHttp('http://127.0.0.1:8571'), + client: new LemmyHttp("http://127.0.0.1:8571"), auth: None, }; export let epsilon: API = { - client: new LemmyHttp('http://127.0.0.1:8581'), + client: new LemmyHttp("http://127.0.0.1:8581"), auth: None, }; -const password = 'lemmylemmy' +const password = "lemmylemmy"; export async function setupLogins() { let formAlpha = new Login({ - username_or_email: 'lemmy_alpha', + username_or_email: "lemmy_alpha", password, }); let resAlpha = alpha.client.login(formAlpha); let formBeta = new Login({ - username_or_email: 'lemmy_beta', + username_or_email: "lemmy_beta", password, }); let resBeta = beta.client.login(formBeta); let formGamma = new Login({ - username_or_email: 'lemmy_gamma', + username_or_email: "lemmy_gamma", password, }); let resGamma = gamma.client.login(formGamma); let formDelta = new Login({ - username_or_email: 'lemmy_delta', + username_or_email: "lemmy_delta", password, }); let resDelta = delta.client.login(formDelta); let formEpsilon = new Login({ - username_or_email: 'lemmy_epsilon', + username_or_email: "lemmy_epsilon", password, }); let resEpsilon = epsilon.client.login(formEpsilon); @@ -145,6 +145,8 @@ export async function setupLogins() { // Registration applications are now enabled by default, need to disable them let editSiteForm = new EditSite({ + require_application: Some(false), + federation_debug: Some(true), name: None, sidebar: None, description: None, @@ -155,23 +157,74 @@ export async function setupLogins() { enable_nsfw: None, community_creation_admin_only: None, require_email_verification: None, - require_application: Some(false), application_question: None, private_instance: None, default_theme: None, - legal_information: None, default_post_listing_type: None, + legal_information: None, + application_email_admins: None, + hide_modlog_mod_names: None, + discussion_languages: None, + slur_filter_regex: None, + actor_name_max_length: None, + rate_limit_message: Some(999), + rate_limit_message_per_second: None, + rate_limit_post: Some(999), + rate_limit_post_per_second: None, + rate_limit_register: Some(999), + rate_limit_register_per_second: None, + rate_limit_image: Some(999), + rate_limit_image_per_second: None, + rate_limit_comment: Some(999), + rate_limit_comment_per_second: None, + rate_limit_search: Some(999), + rate_limit_search_per_second: None, + federation_enabled: None, + federation_strict_allowlist: None, + federation_http_fetch_retry_limit: None, + federation_worker_count: None, + captcha_enabled: None, + captcha_difficulty: None, + allowed_instances: None, + blocked_instances: None, auth: "", }); + + // Set the blocks and auths for each editSiteForm.auth = alpha.auth.unwrap(); + editSiteForm.allowed_instances = Some([ + "lemmy-beta", + "lemmy-gamma", + "lemmy-delta", + "lemmy-epsilon", + ]); await alpha.client.editSite(editSiteForm); + editSiteForm.auth = beta.auth.unwrap(); + editSiteForm.allowed_instances = Some([ + "lemmy-alpha", + "lemmy-gamma", + "lemmy-delta", + "lemmy-epsilon", + ]); await beta.client.editSite(editSiteForm); + editSiteForm.auth = gamma.auth.unwrap(); + editSiteForm.allowed_instances = Some([ + "lemmy-alpha", + "lemmy-beta", + "lemmy-delta", + "lemmy-epsilon", + ]); await gamma.client.editSite(editSiteForm); + + editSiteForm.allowed_instances = Some(["lemmy-beta"]); editSiteForm.auth = delta.auth.unwrap(); await delta.client.editSite(editSiteForm); + editSiteForm.auth = epsilon.auth.unwrap(); + editSiteForm.allowed_instances = Some([]); + editSiteForm.blocked_instances = Some(["lemmy-alpha"]); await epsilon.client.editSite(editSiteForm); // Create the main alpha/beta communities @@ -185,7 +238,7 @@ export async function createPost( ): Promise { let name = randomString(5); let body = Some(randomString(10)); - let url = Some('https://google.com/'); + let url = Some("https://google.com/"); let form = new CreatePost({ name, url, @@ -194,12 +247,13 @@ export async function createPost( community_id, nsfw: None, honeypot: None, + language_id: None, }); return api.client.createPost(form); } export async function editPost(api: API, post: Post): Promise { - let name = Some('A jest test federated post, updated'); + let name = Some("A jest test federated post, updated"); let form = new EditPost({ name, post_id: post.id, @@ -207,6 +261,7 @@ export async function editPost(api: API, post: Post): Promise { nsfw: None, url: None, body: None, + language_id: None, }); return api.client.editPost(form); } @@ -342,7 +397,7 @@ export async function resolveBetaCommunity( ): Promise { // Use short-hand search url let form = new ResolveObject({ - q: '!main@lemmy-beta:8551', + q: "!main@lemmy-beta:8551", auth: api.auth, }); return api.client.resolveObject(form); @@ -415,7 +470,7 @@ export async function followCommunity( let form = new FollowCommunity({ community_id, follow, - auth: api.auth.unwrap() + auth: api.auth.unwrap(), }); return api.client.followCommunity(form); } @@ -428,7 +483,7 @@ export async function likePost( let form = new CreatePostLike({ post_id: post.id, score: score, - auth: api.auth.unwrap() + auth: api.auth.unwrap(), }); return api.client.likePost(form); @@ -438,13 +493,14 @@ export async function createComment( api: API, post_id: number, parent_id: Option, - content = 'a jest test comment' + content = "a jest test comment" ): Promise { let form = new CreateComment({ content, post_id, parent_id, form_id: None, + language_id: None, auth: api.auth.unwrap(), }); return api.client.createComment(form); @@ -453,13 +509,15 @@ export async function createComment( export async function editComment( api: API, comment_id: number, - content = 'A jest test federated comment update' + content = Some("A jest test federated comment update") ): Promise { let form = new EditComment({ content, comment_id, form_id: None, - auth: api.auth.unwrap() + language_id: None, + distinguished: None, + auth: api.auth.unwrap(), }); return api.client.editComment(form); } @@ -491,7 +549,9 @@ export async function removeComment( return api.client.removeComment(form); } -export async function getMentions(api: API): Promise { +export async function getMentions( + api: API +): Promise { let form = new GetPersonMentions({ sort: Some(CommentSortType.New), unread_only: Some(false), @@ -519,7 +579,7 @@ export async function createCommunity( api: API, name_: string = randomString(5) ): Promise { - let description = Some('a sample description'); + let description = Some("a sample description"); let form = new CreateCommunity({ name: name_, title: name_, @@ -577,7 +637,7 @@ export async function createPrivateMessage( api: API, recipient_id: number ): Promise { - let content = 'A jest test federated private message'; + let content = "A jest test federated private message"; let form = new CreatePrivateMessage({ content, recipient_id, @@ -590,7 +650,7 @@ export async function editPrivateMessage( api: API, private_message_id: number ): Promise { - let updatedContent = 'A jest test federated private message edited'; + let updatedContent = "A jest test federated private message edited"; let form = new EditPrivateMessage({ content: updatedContent, private_message_id, @@ -630,18 +690,18 @@ export async function registerUser( return api.client.register(form); } -export async function saveUserSettingsBio( - api: API -): Promise { +export async function saveUserSettingsBio(api: API): Promise { let form = new SaveUserSettings({ show_nsfw: Some(true), - theme: Some('darkly'), + theme: Some("darkly"), default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Active)), - default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)), - lang: Some('en'), + default_listing_type: Some( + Object.keys(ListingType).indexOf(ListingType.All) + ), + interface_language: Some("en"), show_avatars: Some(true), send_notifications_to_email: Some(false), - bio: Some('a changed bio'), + bio: Some("a changed bio"), avatar: None, banner: None, display_name: None, @@ -652,6 +712,7 @@ export async function saveUserSettingsBio( show_bot_accounts: None, show_new_post_notifs: None, bot_account: None, + discussion_languages: None, auth: api.auth.unwrap(), }); return saveUserSettings(api, form); @@ -660,18 +721,20 @@ export async function saveUserSettingsBio( export async function saveUserSettingsFederated( api: API ): Promise { - let avatar = Some('https://image.flaticon.com/icons/png/512/35/35896.png'); - let banner = Some('https://image.flaticon.com/icons/png/512/36/35896.png'); - let bio = Some('a changed bio'); + let avatar = Some("https://image.flaticon.com/icons/png/512/35/35896.png"); + let banner = Some("https://image.flaticon.com/icons/png/512/36/35896.png"); + let bio = Some("a changed bio"); let form = new SaveUserSettings({ show_nsfw: Some(false), - theme: Some(''), + theme: Some(""), default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Hot)), - default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)), - lang: Some(''), + default_listing_type: Some( + Object.keys(ListingType).indexOf(ListingType.All) + ), + interface_language: Some(""), avatar, banner, - display_name: Some('user321'), + display_name: Some("user321"), show_avatars: Some(false), send_notifications_to_email: Some(false), bio, @@ -682,6 +745,7 @@ export async function saveUserSettingsFederated( bot_account: None, show_bot_accounts: None, show_new_post_notifs: None, + discussion_languages: None, auth: api.auth.unwrap(), }); return await saveUserSettings(alpha, form); @@ -694,19 +758,15 @@ export async function saveUserSettings( return api.client.saveUserSettings(form); } -export async function deleteUser( - api: API -): Promise { +export async function deleteUser(api: API): Promise { let form = new DeleteAccount({ auth: api.auth.unwrap(), - password + password, }); return api.client.deleteAccount(form); } -export async function getSite( - api: API -): Promise { +export async function getSite(api: API): Promise { let form = new GetSite({ auth: api.auth, }); @@ -725,14 +785,12 @@ export async function listPrivateMessages( return api.client.getPrivateMessages(form); } -export async function unfollowRemotes( - api: API -): Promise { +export async function unfollowRemotes(api: API): Promise { // Unfollow all remote communities let site = await getSite(api); - let remoteFollowed = site.my_user.unwrap().follows.filter( - c => c.community.local == false - ); + let remoteFollowed = site.my_user + .unwrap() + .follows.filter(c => c.community.local == false); for (let cu of remoteFollowed) { await followCommunity(api, false, cu.community.id); } @@ -743,7 +801,11 @@ export async function unfollowRemotes( export async function followBeta(api: API): Promise { let betaCommunity = (await resolveBetaCommunity(api)).community; if (betaCommunity.isSome()) { - let follow = await followCommunity(api, true, betaCommunity.unwrap().community.id); + let follow = await followCommunity( + api, + true, + betaCommunity.unwrap().community.id + ); return follow; } else { return Promise.reject("no community worked"); @@ -763,7 +825,9 @@ export async function reportPost( return api.client.createPostReport(form); } -export async function listPostReports(api: API): Promise { +export async function listPostReports( + api: API +): Promise { let form = new ListPostReports({ auth: api.auth.unwrap(), page: None, @@ -787,7 +851,9 @@ export async function reportComment( return api.client.createCommentReport(form); } -export async function listCommentReports(api: API): Promise { +export async function listCommentReports( + api: API +): Promise { let form = new ListCommentReports({ page: None, limit: None, @@ -798,7 +864,7 @@ export async function listCommentReports(api: API): Promise setTimeout(resolve, millis)); } @@ -811,8 +877,9 @@ export function wrapper(form: any): string { } export function randomString(length: number): string { - var result = ''; - var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_'; + var result = ""; + var characters = + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; var charactersLength = characters.length; for (var i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)); diff --git a/api_tests/src/user.spec.ts b/api_tests/src/user.spec.ts index 3dd66e58..cd132518 100644 --- a/api_tests/src/user.spec.ts +++ b/api_tests/src/user.spec.ts @@ -1,8 +1,6 @@ jest.setTimeout(120000); -import {None} from '@sniptt/monads'; -import { - PersonViewSafe, -} from 'lemmy-js-client'; +import { None } from "@sniptt/monads"; +import { PersonViewSafe } from "lemmy-js-client"; import { alpha, @@ -20,7 +18,7 @@ import { resolveComment, saveUserSettingsFederated, setupLogins, -} from './shared'; +} from "./shared"; beforeAll(async () => { await setupLogins(); @@ -28,59 +26,82 @@ beforeAll(async () => { let apShortname: string; -function assertUserFederation(userOne: PersonViewSafe, userTwo: PersonViewSafe) { +function assertUserFederation( + userOne: PersonViewSafe, + userTwo: PersonViewSafe +) { expect(userOne.person.name).toBe(userTwo.person.name); - expect(userOne.person.display_name.unwrapOr("none")).toBe(userTwo.person.display_name.unwrapOr("none")); - expect(userOne.person.bio.unwrapOr("none")).toBe(userTwo.person.bio.unwrapOr("none")); + expect(userOne.person.display_name.unwrapOr("none")).toBe( + userTwo.person.display_name.unwrapOr("none") + ); + expect(userOne.person.bio.unwrapOr("none")).toBe( + userTwo.person.bio.unwrapOr("none") + ); expect(userOne.person.actor_id).toBe(userTwo.person.actor_id); - expect(userOne.person.avatar.unwrapOr("none")).toBe(userTwo.person.avatar.unwrapOr("none")); - expect(userOne.person.banner.unwrapOr("none")).toBe(userTwo.person.banner.unwrapOr("none")); + expect(userOne.person.avatar.unwrapOr("none")).toBe( + userTwo.person.avatar.unwrapOr("none") + ); + expect(userOne.person.banner.unwrapOr("none")).toBe( + userTwo.person.banner.unwrapOr("none") + ); expect(userOne.person.published).toBe(userTwo.person.published); } -test('Create user', async () => { +test("Create user", async () => { let userRes = await registerUser(alpha); expect(userRes.jwt).toBeDefined(); alpha.auth = userRes.jwt; - + let site = await getSite(alpha); expect(site.my_user).toBeDefined(); - apShortname = `@${site.my_user.unwrap().local_user_view.person.name}@lemmy-alpha:8541`; + apShortname = `@${ + site.my_user.unwrap().local_user_view.person.name + }@lemmy-alpha:8541`; }); -test('Set some user settings, check that they are federated', async () => { +test("Set some user settings, check that they are federated", async () => { await saveUserSettingsFederated(alpha); let alphaPerson = (await resolvePerson(alpha, apShortname)).person.unwrap(); let betaPerson = (await resolvePerson(beta, apShortname)).person.unwrap(); assertUserFederation(alphaPerson, betaPerson); }); -test('Delete user', async () => { +test("Delete user", async () => { let userRes = await registerUser(alpha); expect(userRes.jwt).toBeDefined(); let user: API = { client: alpha.client, - auth: userRes.jwt - } + auth: userRes.jwt, + }; // make a local post and comment - let alphaCommunity = (await resolveCommunity(user, '!main@lemmy-alpha:8541')).community.unwrap(); - let localPost = (await createPost(user, alphaCommunity.community.id)).post_view.post; + let alphaCommunity = ( + await resolveCommunity(user, "!main@lemmy-alpha:8541") + ).community.unwrap(); + let localPost = (await createPost(user, alphaCommunity.community.id)) + .post_view.post; expect(localPost).toBeDefined(); - let localComment = (await createComment(user, localPost.id, None)).comment_view.comment; + let localComment = (await createComment(user, localPost.id, None)) + .comment_view.comment; expect(localComment).toBeDefined(); // make a remote post and comment let betaCommunity = (await resolveBetaCommunity(user)).community.unwrap(); - let remotePost = (await createPost(user, betaCommunity.community.id)).post_view.post; + let remotePost = (await createPost(user, betaCommunity.community.id)) + .post_view.post; expect(remotePost).toBeDefined(); - let remoteComment = (await createComment(user, remotePost.id, None)).comment_view.comment; + let remoteComment = (await createComment(user, remotePost.id, None)) + .comment_view.comment; expect(remoteComment).toBeDefined(); await deleteUser(user); expect((await resolvePost(alpha, localPost)).post.isNone()).toBe(true); - expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe(true) - expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true) - expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe(true) + expect((await resolveComment(alpha, localComment)).comment.isNone()).toBe( + true + ); + expect((await resolvePost(alpha, remotePost)).post.isNone()).toBe(true); + expect((await resolveComment(alpha, remoteComment)).comment.isNone()).toBe( + true + ); }); diff --git a/api_tests/yarn.lock b/api_tests/yarn.lock index 97ec5c37..9797ac69 100644 --- a/api_tests/yarn.lock +++ b/api_tests/yarn.lock @@ -17,7 +17,7 @@ dependencies: "@babel/highlight" "^7.10.4" -"@babel/code-frame@^7.12.11", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": +"@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== @@ -50,7 +50,7 @@ json5 "^2.2.1" semver "^6.3.0" -"@babel/generator@^7.12.11", "@babel/generator@^7.18.9", "@babel/generator@^7.7.2": +"@babel/generator@^7.18.9", "@babel/generator@^7.7.2": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.9.tgz#68337e9ea8044d6ddc690fb29acae39359cca0a5" integrity sha512-wt5Naw6lJrL1/SGkipMiFxJjtyczUWTP38deiP1PO60HsBjDeKk08CGC3S8iVuvf0FmTdgKwU1KIXzSKL1G0Ug== @@ -74,7 +74,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== -"@babel/helper-function-name@^7.12.11", "@babel/helper-function-name@^7.18.9": +"@babel/helper-function-name@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== @@ -122,14 +122,14 @@ dependencies: "@babel/types" "^7.18.6" -"@babel/helper-split-export-declaration@^7.12.11", "@babel/helper-split-export-declaration@^7.18.6": +"@babel/helper-split-export-declaration@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== dependencies: "@babel/types" "^7.18.6" -"@babel/helper-validator-identifier@^7.12.11", "@babel/helper-validator-identifier@^7.15.7", "@babel/helper-validator-identifier@^7.18.6": +"@babel/helper-validator-identifier@^7.18.6": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== @@ -157,16 +157,11 @@ chalk "^2.0.0" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.12.11", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.6", "@babel/parser@^7.18.9": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.9.tgz#f2dde0c682ccc264a9a8595efd030a5cc8fd2539" integrity sha512-9uJveS9eY9DJ0t64YbIBZICtJy8a5QrDEVdiLCG97fVLpDTpGX7t8mMSb6OWw6Lrnjqj4O8zwjELX3dhoMgiBg== -"@babel/parser@^7.7.0": - version "7.12.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.12.11.tgz#9ce3595bcd74bc5c466905e86c535b8b25011e79" - integrity sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg== - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -258,28 +253,6 @@ dependencies: "@babel/helper-plugin-utils" "^7.18.6" -"@babel/runtime-corejs3@^7.10.2": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.12.5.tgz#ffee91da0eb4c6dae080774e94ba606368e414f4" - integrity sha512-roGr54CsTmNPPzZoCP1AmDXuBoNao7tnSA83TXTwt+UK5QVyh1DIJnrgYRPWKCF2flqZQXwa7Yr8v7VmLzF0YQ== - dependencies: - core-js-pure "^3.0.0" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.10.2": - version "7.12.5" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.12.5.tgz#410e7e487441e1b360c29be715d870d9b985882e" - integrity sha512-plcc+hbExy3McchJCEQG3knOsuh3HH+Prx1P6cLIkET/0dLuQDEnrT+s27Axgc9bqfsmNUNHfscgMUdBpC9xfg== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.16.3": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - "@babel/template@^7.18.6", "@babel/template@^7.3.3": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.6.tgz#1283f4993e00b929d6e2d3c72fdc9168a2977a31" @@ -305,22 +278,7 @@ debug "^4.1.0" globals "^11.1.0" -"@babel/traverse@^7.7.0": - version "7.12.12" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.12.12.tgz#d0cd87892704edd8da002d674bc811ce64743376" - integrity sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w== - dependencies: - "@babel/code-frame" "^7.12.11" - "@babel/generator" "^7.12.11" - "@babel/helper-function-name" "^7.12.11" - "@babel/helper-split-export-declaration" "^7.12.11" - "@babel/parser" "^7.12.11" - "@babel/types" "^7.12.12" - debug "^4.1.0" - globals "^11.1.0" - lodash "^4.17.19" - -"@babel/types@^7.0.0", "@babel/types@^7.12.12", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": +"@babel/types@^7.0.0", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": version "7.18.9" resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.9.tgz#7148d64ba133d8d73a41b3172ac4b83a1452205f" integrity sha512-WwMLAg2MvJmt/rKEVQBBhIVffMmnilX4oe0sRe7iPOHIGsqpruFHHdrfj4O1CMMtgMtCU4oPafZjDPCRgO57Wg== @@ -328,28 +286,19 @@ "@babel/helper-validator-identifier" "^7.18.6" to-fast-properties "^2.0.0" -"@babel/types@^7.7.0": - version "7.12.12" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.12.12.tgz#4608a6ec313abbd87afa55004d373ad04a96c299" - integrity sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ== - dependencies: - "@babel/helper-validator-identifier" "^7.12.11" - lodash "^4.17.19" - to-fast-properties "^2.0.0" - "@bcoe/v8-coverage@^0.2.3": version "0.2.3" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@eslint/eslintrc@^1.3.0": - version "1.3.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.0.tgz#29f92c30bb3e771e4a2048c95fa6855392dfac4f" - integrity sha512-UWW0TMTmk2d7hLcWD1/e2g5HDM/HQ3csaLSqXCfqwh4uNDuNqlaKWXmEsL4Cs41Z0KnILNvwbHAah3C2yt06kw== +"@eslint/eslintrc@^1.3.3": + version "1.3.3" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" + integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== dependencies: ajv "^6.12.4" debug "^4.3.2" - espree "^9.3.2" + espree "^9.4.0" globals "^13.15.0" ignore "^5.2.0" import-fresh "^3.2.1" @@ -357,15 +306,20 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@humanwhocodes/config-array@^0.9.2": - version "0.9.5" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7" - integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw== +"@humanwhocodes/config-array@^0.10.5": + version "0.10.7" + resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.10.7.tgz#6d53769fd0c222767e6452e8ebda825c22e9f0dc" + integrity sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w== dependencies: "@humanwhocodes/object-schema" "^1.2.1" debug "^4.1.1" minimatch "^3.0.4" +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + "@humanwhocodes/object-schema@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" @@ -724,26 +678,21 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3" integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ== -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4= - "@types/node@*": version "18.6.1" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.6.1.tgz#828e4785ccca13f44e2fb6852ae0ef11e3e20ba5" integrity sha512-z+2vB6yDt1fNwKOeGbckpmirO+VBDuQqecXkgeIqDlaOtmKn6hPR/viQ8cxCfqLU4fTlvM3+YjM367TukWdxpg== -"@types/normalize-package-data@^2.4.0": - version "2.4.0" - resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz#e486d0d97396d79beedd0a6e33f4534ff6b4973e" - integrity sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA== - "@types/prettier@^2.1.5": version "2.6.4" resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.6.4.tgz#ad899dad022bab6b5a9f0a0fe67c2f7a4a8950ed" integrity sha512-fOwvpvQYStpb/zHMx0Cauwywu9yLDmzWiiQBC7gJyq5tYLUXFZvDG7VK1B7WBxxjBJNKFOZ0zLoOQn8vmATbhw== +"@types/semver@^7.3.12": + version "7.3.12" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.3.12.tgz#920447fdd78d76b19de0438b7f60df3c4a80bf1c" + integrity sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A== + "@types/stack-utils@^2.0.0": version "2.0.1" resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" @@ -768,139 +717,88 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.0.tgz#382182d5cb062f52aac54434cfc47c28898c8006" - integrity sha512-qT4lr2jysDQBQOPsCCvpPUZHjbABoTJW8V9ZzIYKHMfppJtpdtzszDYsldwhFxlhvrp7aCHeXD1Lb9M1zhwWwQ== +"@typescript-eslint/eslint-plugin@^5.21.0": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.40.1.tgz#3203a6ff396b1194083faaa6e5110c401201d7d5" + integrity sha512-FsWboKkWdytGiXT5O1/R9j37YgcjO8MKHSUmWnIEjVaz0krHkplPnYi7mwdb+5+cs0toFNQb0HIrN7zONdIEWg== dependencies: - "@typescript-eslint/experimental-utils" "5.9.0" - "@typescript-eslint/scope-manager" "5.9.0" - "@typescript-eslint/type-utils" "5.9.0" - debug "^4.3.2" - functional-red-black-tree "^1.0.1" - ignore "^5.1.8" + "@typescript-eslint/scope-manager" "5.40.1" + "@typescript-eslint/type-utils" "5.40.1" + "@typescript-eslint/utils" "5.40.1" + debug "^4.3.4" + ignore "^5.2.0" regexpp "^3.2.0" - semver "^7.3.5" + semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/experimental-utils@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.0.tgz#652762d37d6565ef07af285021b8347b6c79a827" - integrity sha512-ZnLVjBrf26dn7ElyaSKa6uDhqwvAi4jBBmHK1VxuFGPRAxhdi18ubQYSGA7SRiFiES3q9JiBOBHEBStOFkwD2g== +"@typescript-eslint/parser@^5.21.0": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.40.1.tgz#e7f8295dd8154d0d37d661ddd8e2f0ecfdee28dd" + integrity sha512-IK6x55va5w4YvXd4b3VrXQPldV9vQTxi5ov+g4pMANsXPTXOcfjx08CRR1Dfrcc51syPtXHF5bgLlMHYFrvQtg== dependencies: - "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.9.0" - "@typescript-eslint/types" "5.9.0" - "@typescript-eslint/typescript-estree" "5.9.0" - eslint-scope "^5.1.1" - eslint-utils "^3.0.0" - -"@typescript-eslint/experimental-utils@^5.0.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.31.0.tgz#f2b23ebeebd31358ce44cf5be7c3411127627b84" - integrity sha512-Yiar0ggNPyOsvrslJBdOo3jc3wjI6NnmWOQBA8WhR54YPbVqTNLuuHC6zxEt8FIgMozerxRlAncwznEjK+cJVA== - dependencies: - "@typescript-eslint/utils" "5.31.0" - -"@typescript-eslint/parser@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.9.0.tgz#fdbb08767a4caa6ca6ccfed5f9ffe9387f0c7d97" - integrity sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ== - dependencies: - "@typescript-eslint/scope-manager" "5.9.0" - "@typescript-eslint/types" "5.9.0" - "@typescript-eslint/typescript-estree" "5.9.0" - debug "^4.3.2" - -"@typescript-eslint/scope-manager@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.31.0.tgz#f47a794ba84d9b818ab7f8f44fff55a61016c606" - integrity sha512-8jfEzBYDBG88rcXFxajdVavGxb5/XKXyvWgvD8Qix3EEJLCFIdVloJw+r9ww0wbyNLOTYyBsR+4ALNGdlalLLg== - dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/scope-manager" "5.40.1" + "@typescript-eslint/types" "5.40.1" + "@typescript-eslint/typescript-estree" "5.40.1" + debug "^4.3.4" -"@typescript-eslint/scope-manager@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.9.0.tgz#02dfef920290c1dcd7b1999455a3eaae7a1a3117" - integrity sha512-DKtdIL49Qxk2a8icF6whRk7uThuVz4A6TCXfjdJSwOsf+9ree7vgQWcx0KOyCdk0i9ETX666p4aMhrRhxhUkyg== +"@typescript-eslint/scope-manager@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.40.1.tgz#a7a5197dfd234622a2421ea590ee0ccc02e18dfe" + integrity sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg== dependencies: - "@typescript-eslint/types" "5.9.0" - "@typescript-eslint/visitor-keys" "5.9.0" + "@typescript-eslint/types" "5.40.1" + "@typescript-eslint/visitor-keys" "5.40.1" -"@typescript-eslint/type-utils@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.9.0.tgz#fd5963ead04bc9b7af9c3a8e534d8d39f1ce5f93" - integrity sha512-uVCb9dJXpBrK1071ri5aEW7ZHdDHAiqEjYznF3HSSvAJXyrkxGOw2Ejibz/q6BXdT8lea8CMI0CzKNFTNI6TEQ== +"@typescript-eslint/type-utils@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.40.1.tgz#091e4ce3bebbdb68f4980bae9dee2e4e1725f601" + integrity sha512-DLAs+AHQOe6n5LRraXiv27IYPhleF0ldEmx6yBqBgBLaNRKTkffhV1RPsjoJBhVup2zHxfaRtan8/YRBgYhU9Q== dependencies: - "@typescript-eslint/experimental-utils" "5.9.0" - debug "^4.3.2" + "@typescript-eslint/typescript-estree" "5.40.1" + "@typescript-eslint/utils" "5.40.1" + debug "^4.3.4" tsutils "^3.21.0" -"@typescript-eslint/types@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.31.0.tgz#7aa389122b64b18e473c1672fb3b8310e5f07a9a" - integrity sha512-/f/rMaEseux+I4wmR6mfpM2wvtNZb1p9hAV77hWfuKc3pmaANp5dLAZSiE3/8oXTYTt3uV9KW5yZKJsMievp6g== +"@typescript-eslint/types@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.40.1.tgz#de37f4f64de731ee454bb2085d71030aa832f749" + integrity sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw== -"@typescript-eslint/types@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.9.0.tgz#e5619803e39d24a03b3369506df196355736e1a3" - integrity sha512-mWp6/b56Umo1rwyGCk8fPIzb9Migo8YOniBGPAQDNC6C52SeyNGN4gsVwQTAR+RS2L5xyajON4hOLwAGwPtUwg== - -"@typescript-eslint/typescript-estree@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.31.0.tgz#eb92970c9d6e3946690d50c346fb9b1d745ee882" - integrity sha512-3S625TMcARX71wBc2qubHaoUwMEn+l9TCsaIzYI/ET31Xm2c9YQ+zhGgpydjorwQO9pLfR/6peTzS/0G3J/hDw== +"@typescript-eslint/typescript-estree@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz#9a7d25492f02c69882ce5e0cd1857b0c55645d72" + integrity sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA== dependencies: - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/visitor-keys" "5.31.0" + "@typescript-eslint/types" "5.40.1" + "@typescript-eslint/visitor-keys" "5.40.1" debug "^4.3.4" globby "^11.1.0" is-glob "^4.0.3" semver "^7.3.7" tsutils "^3.21.0" -"@typescript-eslint/typescript-estree@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.0.tgz#0e5c6f03f982931abbfbc3c1b9df5fbf92a3490f" - integrity sha512-kxo3xL2mB7XmiVZcECbaDwYCt3qFXz99tBSuVJR4L/sR7CJ+UNAPrYILILktGj1ppfZ/jNt/cWYbziJUlHl1Pw== - dependencies: - "@typescript-eslint/types" "5.9.0" - "@typescript-eslint/visitor-keys" "5.9.0" - debug "^4.3.2" - globby "^11.0.4" - is-glob "^4.0.3" - semver "^7.3.5" - tsutils "^3.21.0" - -"@typescript-eslint/utils@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.31.0.tgz#e146fa00dca948bfe547d665b2138a2dc1b79acd" - integrity sha512-kcVPdQS6VIpVTQ7QnGNKMFtdJdvnStkqS5LeALr4rcwx11G6OWb2HB17NMPnlRHvaZP38hL9iK8DdE9Fne7NYg== +"@typescript-eslint/utils@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-5.40.1.tgz#3204fb73a559d3b7bab7dc9d3c44487c2734a9ca" + integrity sha512-a2TAVScoX9fjryNrW6BZRnreDUszxqm9eQ9Esv8n5nXApMW0zeANUYlwh/DED04SC/ifuBvXgZpIK5xeJHQ3aw== dependencies: "@types/json-schema" "^7.0.9" - "@typescript-eslint/scope-manager" "5.31.0" - "@typescript-eslint/types" "5.31.0" - "@typescript-eslint/typescript-estree" "5.31.0" + "@types/semver" "^7.3.12" + "@typescript-eslint/scope-manager" "5.40.1" + "@typescript-eslint/types" "5.40.1" + "@typescript-eslint/typescript-estree" "5.40.1" eslint-scope "^5.1.1" eslint-utils "^3.0.0" + semver "^7.3.7" -"@typescript-eslint/visitor-keys@5.31.0": - version "5.31.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.31.0.tgz#b0eca264df01ce85dceb76aebff3784629258f54" - integrity sha512-ZK0jVxSjS4gnPirpVjXHz7mgdOsZUHzNYSfTw2yPa3agfbt9YfqaBiBZFSSxeBWnpWkzCxTfUpnzA3Vily/CSg== +"@typescript-eslint/visitor-keys@5.40.1": + version "5.40.1" + resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz#f3d2bf5af192f4432b84cec6fdcb387193518754" + integrity sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw== dependencies: - "@typescript-eslint/types" "5.31.0" + "@typescript-eslint/types" "5.40.1" eslint-visitor-keys "^3.3.0" -"@typescript-eslint/visitor-keys@5.9.0": - version "5.9.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz#7585677732365e9d27f1878150fab3922784a1a6" - integrity sha512-6zq0mb7LV0ThExKlecvpfepiB+XEtFv/bzx7/jKSgyXTFD7qjmSu1FoiS0x3OZaiS+UIXpH2vd9O89f02RCtgw== - dependencies: - "@typescript-eslint/types" "5.9.0" - eslint-visitor-keys "^3.0.0" - abab@^2.0.3, abab@^2.0.5: version "2.0.6" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" @@ -929,7 +827,7 @@ acorn@^7.1.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.7.1: +acorn@^8.2.4, acorn@^8.8.0: version "8.8.0" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== @@ -1002,93 +900,16 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -array-includes@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.2.tgz#a8db03e0b88c8c6aeddc49cb132f9bcab4ebf9c8" - integrity sha512-w2GspexNQpx+PutG3QpT437/BenZBj0M/MZGn5mzv/MofYqo0xmRHzn4lFsoDlWJ+THYsGJmFlW68WlDFx7VRw== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - es-abstract "^1.18.0-next.1" - get-intrinsic "^1.0.1" - is-string "^1.0.5" - -array-includes@^3.1.4, array-includes@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - array-union@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== -array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha1-9wtzXGvKGlycItmCw+Oef+ujva0= - asynckit@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -axe-core@^4.3.5: - version "4.4.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" - integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -babel-eslint@10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232" - integrity sha512-ifWaTHQ0ce+448CYop8AdrQiBsGrnC+bMgfyKFdi6EsPLTAWG+QfyDeM6OH+FmWnKvEq5NnBMLvlBUPKQZoDSg== - dependencies: - "@babel/code-frame" "^7.0.0" - "@babel/parser" "^7.7.0" - "@babel/traverse" "^7.7.0" - "@babel/types" "^7.7.0" - eslint-visitor-keys "^1.0.0" - resolve "^1.12.0" - babel-jest@^27.5.1: version "27.5.1" resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-27.5.1.tgz#a1bf8d61928edfefd21da27eb86a695bfd691444" @@ -1204,27 +1025,6 @@ buffer-from@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== -builtin-modules@^3.0.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" - integrity sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw== - -call-bind@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.0.tgz#24127054bb3f9bdcb4b1fb82418186072f77b8ce" - integrity sha512-AEXsYIyyDY3MCzbwdhzG3Jx1R0J2wetQyUynn6dYHAO+bg8l1k7jwZtRv4ryryFs7EP+NDlikJlVe59jr0cM2w== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.0" - -call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -1267,7 +1067,7 @@ char-regex@^1.0.2: resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== -ci-info@^3.2.0, ci-info@^3.3.0: +ci-info@^3.2.0: version "3.3.2" resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== @@ -1282,13 +1082,6 @@ class-transformer@^0.5.1: resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.5.1.tgz#24147d5dffd2a6cea930a3250a677addf96ab336" integrity sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw== -clean-regexp@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clean-regexp/-/clean-regexp-1.0.0.tgz#8df7c7aae51fd36874e8f8d05b9180bc11a3fed7" - integrity sha1-jffHquUf02h06PjQW5GAvBGj/tc= - dependencies: - escape-string-regexp "^1.0.5" - cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1351,11 +1144,6 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" -core-js-pure@^3.0.0: - version "3.8.2" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.8.2.tgz#286f885c0dac1cdcd6d78397392abc25ddeca225" - integrity sha512-v6zfIQqL/pzTVAbZvYUozsxNfxcFb6Ks3ZfEbuneJl3FW9Jb8F6vLWB6f+qTmAu72msUdyb84V8d/yBFf7FNnw== - cross-spawn@^7.0.2, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -1382,11 +1170,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -damerau-levenshtein@^1.0.7: - version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - data-urls@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-2.0.0.tgz#156485a72963a970f5d5821aaf642bef2bf2db9b" @@ -1403,20 +1186,6 @@ debug@4, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: dependencies: ms "2.1.2" -debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - decimal.js@^10.2.1: version "10.3.1" resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" @@ -1442,21 +1211,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -define-properties@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" - integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ== - dependencies: - object-keys "^1.0.12" - -define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -1484,13 +1238,6 @@ dir-glob@^3.0.1: dependencies: path-type "^4.0.0" -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - doctrine@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" @@ -1520,11 +1267,6 @@ emoji-regex@^8.0.0: resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1532,69 +1274,6 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.18.0-next.1: - version "1.18.0-next.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.0-next.1.tgz#6e3a0a4bda717e5023ab3b8e90bec36108d22c68" - integrity sha512-I4UGspA0wpZXWENrdA0uHbnhte683t3qT/1VFH9aX2dA5PPSf6QW5HHXf5HImaqPmjXaVeVk4RGWnaylmV7uAA== - dependencies: - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - is-callable "^1.2.2" - is-negative-zero "^2.0.0" - is-regex "^1.1.1" - object-inspect "^1.8.0" - object-keys "^1.1.1" - object.assign "^4.1.1" - string.prototype.trimend "^1.0.1" - string.prototype.trimstart "^1.0.1" - -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.3" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - escalade@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" @@ -1627,180 +1306,13 @@ escodegen@^2.0.0: optionalDependencies: source-map "~0.6.1" -eslint-config-prettier@8.3.0: - version "8.3.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.3.0.tgz#f7471b20b6fe8a9a9254cc684454202886a2dd7a" - integrity sha512-BgZuLUSeKzvlL/VUjx/Yb787VQ26RU3gGjA3iiFvdsp/2bMfVIWUVP7tjxtjS0e+HP409cPlPvNkQloz8C91ew== - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-module-utils@^2.7.2: - version "2.7.3" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee" - integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ== - dependencies: - debug "^3.2.7" - find-up "^2.1.0" - -eslint-plugin-babel@5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-babel/-/eslint-plugin-babel-5.3.1.tgz#75a2413ffbf17e7be57458301c60291f2cfbf560" - integrity sha512-VsQEr6NH3dj664+EyxJwO4FCYm/00JhYb3Sk3ft8o+fpKuIfQ9TaW6uVUfvwMXHcf/lsnRIoyFPsLMyiWCSL/g== - dependencies: - eslint-rule-composer "^0.3.0" - -eslint-plugin-es@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz#75a7cdfdccddc0589934aeeb384175f221c57893" - integrity sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ== - dependencies: - eslint-utils "^2.0.0" - regexpp "^3.0.0" - -eslint-plugin-import@2.25.4: - version "2.25.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.25.4.tgz#322f3f916a4e9e991ac7af32032c25ce313209f1" - integrity sha512-/KJBASVFxpu0xg1kIBn9AUa8hQVnszpwgE7Ld0lKAlx7Ie87yzEzCgSkekt+le/YVhiaosO4Y14GDAOc41nfxA== - dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.2" - has "^1.0.3" - is-core-module "^2.8.0" - is-glob "^4.0.3" - minimatch "^3.0.4" - object.values "^1.1.5" - resolve "^1.20.0" - tsconfig-paths "^3.12.0" - -eslint-plugin-jane@^11.2.2: - version "11.2.2" - resolved "https://registry.yarnpkg.com/eslint-plugin-jane/-/eslint-plugin-jane-11.2.2.tgz#6ad3394d94009e03531fa8a509d147ff88b60303" - integrity sha512-y2yZucJ0g/siHwR5eZbsrAvc0u4qIuc0JeSmR0JgKAbidA6OJZTivgOhyqmeGIBewTBkHpCiqjFzQqB6rD4CnA== - dependencies: - "@typescript-eslint/eslint-plugin" "5.9.0" - "@typescript-eslint/parser" "5.9.0" - babel-eslint "10.1.0" - eslint-config-prettier "8.3.0" - eslint-plugin-babel "5.3.1" - eslint-plugin-import "2.25.4" - eslint-plugin-jest "25.3.4" - eslint-plugin-jsx-a11y "6.5.1" - eslint-plugin-node "11.1.0" - eslint-plugin-prettier "3.4.1" - eslint-plugin-promise "6.0.0" - eslint-plugin-react "7.28.0" - eslint-plugin-react-hooks "4.3.0" - eslint-plugin-unicorn "40.0.0" - -eslint-plugin-jest@25.3.4: - version "25.3.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-jest/-/eslint-plugin-jest-25.3.4.tgz#2031dfe495be1463330f8b80096ddc91f8e6387f" - integrity sha512-CCnwG71wvabmwq/qkz0HWIqBHQxw6pXB1uqt24dxqJ9WB34pVg49bL1sjXphlJHgTMWGhBjN1PicdyxDxrfP5A== - dependencies: - "@typescript-eslint/experimental-utils" "^5.0.0" - -eslint-plugin-jsx-a11y@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.5.1.tgz#cdbf2df901040ca140b6ec14715c988889c2a6d8" - integrity sha512-sVCFKX9fllURnXT2JwLN5Qgo24Ug5NF6dxhkmxsMEUZhXRcGg+X3e1JbJ84YePQKBl5E0ZjAH5Q4rkdcGY99+g== - dependencies: - "@babel/runtime" "^7.16.3" - aria-query "^4.2.2" - array-includes "^3.1.4" - ast-types-flow "^0.0.7" - axe-core "^4.3.5" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.7" - emoji-regex "^9.2.2" - has "^1.0.3" - jsx-ast-utils "^3.2.1" - language-tags "^1.0.5" - minimatch "^3.0.4" - -eslint-plugin-node@11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz#c95544416ee4ada26740a30474eefc5402dc671d" - integrity sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g== - dependencies: - eslint-plugin-es "^3.0.0" - eslint-utils "^2.0.0" - ignore "^5.1.1" - minimatch "^3.0.4" - resolve "^1.10.1" - semver "^6.1.0" - -eslint-plugin-prettier@3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.4.1.tgz#e9ddb200efb6f3d05ffe83b1665a716af4a387e5" - integrity sha512-htg25EUYUeIhKHXjOinK4BgCcDwtLHjqaxCDsMy5nbnUMkKFvIhMVCp+5GFUXQ4Nr8lBsPqtGAqBenbpFqAA2g== +eslint-plugin-prettier@^4.0.0: + version "4.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" + integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== dependencies: prettier-linter-helpers "^1.0.0" -eslint-plugin-promise@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz#017652c07c9816413a41e11c30adc42c3d55ff18" - integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw== - -eslint-plugin-react-hooks@4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.3.0.tgz#318dbf312e06fab1c835a4abef00121751ac1172" - integrity sha512-XslZy0LnMn+84NEG9jSGR6eGqaZB3133L8xewQo3fQagbQuGt7a63gf+P1NGKZavEYEC3UXaWEAA/AqDkuN6xA== - -eslint-plugin-react@7.28.0: - version "7.28.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.28.0.tgz#8f3ff450677571a659ce76efc6d80b6a525adbdf" - integrity sha512-IOlFIRHzWfEQQKcAD4iyYDndHwTQiCMcJVJjxempf203jnNLUnW34AXLrV33+nEXoifJE2ZEGmcjKPL8957eSw== - dependencies: - array-includes "^3.1.4" - array.prototype.flatmap "^1.2.5" - doctrine "^2.1.0" - estraverse "^5.3.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.0.4" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.0" - object.values "^1.1.5" - prop-types "^15.7.2" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.6" - -eslint-plugin-unicorn@40.0.0: - version "40.0.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-unicorn/-/eslint-plugin-unicorn-40.0.0.tgz#a8226cab9c62b303e44fc41b1333a146e5676b86" - integrity sha512-5GRXISfBk8jMmYk1eeNDw8zSRnWTxBjWkzx2Prre6E2/yLu2twozZ3EomLWCBu9nWms/ZE361BItyMQwfnG1qA== - dependencies: - "@babel/helper-validator-identifier" "^7.15.7" - ci-info "^3.3.0" - clean-regexp "^1.0.0" - eslint-utils "^3.0.0" - esquery "^1.4.0" - indent-string "^4.0.0" - is-builtin-module "^3.1.0" - lodash "^4.17.21" - pluralize "^8.0.0" - read-pkg-up "^7.0.1" - regexp-tree "^0.1.24" - safe-regex "^2.1.1" - semver "^7.3.5" - strip-indent "^3.0.0" - -eslint-rule-composer@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz#79320c927b0c5c0d3d3d2b76c8b4a488f25bbaf9" - integrity sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg== - eslint-scope@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" @@ -1817,13 +1329,6 @@ eslint-scope@^7.1.1: esrecurse "^4.3.0" estraverse "^5.2.0" -eslint-utils@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27" - integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg== - dependencies: - eslint-visitor-keys "^1.1.0" - eslint-utils@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" @@ -1831,28 +1336,24 @@ eslint-utils@^3.0.0: dependencies: eslint-visitor-keys "^2.0.0" -eslint-visitor-keys@^1.0.0, eslint-visitor-keys@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e" - integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ== - eslint-visitor-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.0.0.tgz#21fdc8fbcd9c795cc0321f0563702095751511a8" integrity sha512-QudtT6av5WXels9WjIM7qz1XD1cWGvX4gGXvp/zBn9nXG02D0utdU3Em2m/QjTnrsk6bBjmCygl3rmj118msQQ== -eslint-visitor-keys@^3.0.0, eslint-visitor-keys@^3.3.0: +eslint-visitor-keys@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== -eslint@^8.20.0: - version "8.20.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.20.0.tgz#048ac56aa18529967da8354a478be4ec0a2bc81b" - integrity sha512-d4ixhz5SKCa1D6SCPrivP7yYVi7nyD6A4vs6HIAul9ujBzcEmZVM3/0NN/yu5nKhmO1wjp5xQ46iRfmDGlOviA== +eslint@^8.25.0: + version "8.25.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.25.0.tgz#00eb962f50962165d0c4ee3327708315eaa8058b" + integrity sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A== dependencies: - "@eslint/eslintrc" "^1.3.0" - "@humanwhocodes/config-array" "^0.9.2" + "@eslint/eslintrc" "^1.3.3" + "@humanwhocodes/config-array" "^0.10.5" + "@humanwhocodes/module-importer" "^1.0.1" ajv "^6.10.0" chalk "^4.0.0" cross-spawn "^7.0.2" @@ -1862,18 +1363,21 @@ eslint@^8.20.0: eslint-scope "^7.1.1" eslint-utils "^3.0.0" eslint-visitor-keys "^3.3.0" - espree "^9.3.2" + espree "^9.4.0" esquery "^1.4.0" esutils "^2.0.2" fast-deep-equal "^3.1.3" file-entry-cache "^6.0.1" - functional-red-black-tree "^1.0.1" + find-up "^5.0.0" glob-parent "^6.0.1" globals "^13.15.0" + globby "^11.1.0" + grapheme-splitter "^1.0.4" ignore "^5.2.0" import-fresh "^3.0.0" imurmurhash "^0.1.4" is-glob "^4.0.0" + js-sdsl "^4.1.4" js-yaml "^4.1.0" json-stable-stringify-without-jsonify "^1.0.1" levn "^0.4.1" @@ -1885,14 +1389,13 @@ eslint@^8.20.0: strip-ansi "^6.0.1" strip-json-comments "^3.1.0" text-table "^0.2.0" - v8-compile-cache "^2.0.3" -espree@^9.3.2: - version "9.3.2" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.2.tgz#f58f77bd334731182801ced3380a8cc859091596" - integrity sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA== +espree@^9.4.0: + version "9.4.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" + integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== dependencies: - acorn "^8.7.1" + acorn "^8.8.0" acorn-jsx "^5.3.2" eslint-visitor-keys "^3.3.0" @@ -1925,7 +1428,7 @@ estraverse@^5.1.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.2.0.tgz#307df42547e6cc7324d3cf03c155d5cdb8c53880" integrity sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ== -estraverse@^5.2.0, estraverse@^5.3.0: +estraverse@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== @@ -2024,13 +1527,6 @@ fill-range@^7.0.1: dependencies: to-regex-range "^5.0.1" -find-up@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7" - integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c= - dependencies: - locate-path "^2.0.0" - find-up@^4.0.0, find-up@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" @@ -2039,6 +1535,14 @@ find-up@^4.0.0, find-up@^4.1.0: locate-path "^5.0.0" path-exists "^4.0.0" +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + flat-cache@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" @@ -2076,26 +1580,6 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functional-red-black-tree@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" - integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= - -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - gensync@^1.0.0-beta.2: version "1.0.0-beta.2" resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" @@ -2106,24 +1590,6 @@ get-caller-file@^2.0.5: resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== -get-intrinsic@^1.0.0, get-intrinsic@^1.0.1, get-intrinsic@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.0.2.tgz#6820da226e50b24894e08859469dc68361545d49" - integrity sha512-aeX0vrFm21ILl3+JpFFRNe9aUvp6VFZb2/CTbgLb8j75kOhvoNYjt9d8KA/tJG4gSo8nzEDedRl0h7vDmBYRVg== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.1" - -get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -2134,14 +1600,6 @@ get-stream@^6.0.0: resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - glob-parent@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -2180,7 +1638,7 @@ globals@^13.15.0: dependencies: type-fest "^0.20.2" -globby@^11.0.4, globby@^11.1.0: +globby@^11.1.0: version "11.1.0" resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== @@ -2197,10 +1655,10 @@ graceful-fs@^4.2.9: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== +grapheme-splitter@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" + integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== has-flag@^3.0.0: version "3.0.0" @@ -2212,30 +1670,6 @@ has-flag@^4.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" - integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" @@ -2243,11 +1677,6 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" -hosted-git-info@^2.1.4: - version "2.8.8" - resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" - integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg== - html-encoding-sniffer@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz#42a6dc4fd33f00281176e8b23759ca4e4fa185f3" @@ -2289,12 +1718,7 @@ iconv-lite@0.4.24: dependencies: safer-buffer ">= 2.1.2 < 3" -ignore@^5.1.1: - version "5.1.8" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.8.tgz#f150a8b50a34289b33e22f5889abd4d8016f0e57" - integrity sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw== - -ignore@^5.1.8, ignore@^5.2.0: +ignore@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== @@ -2320,11 +1744,6 @@ imurmurhash@^0.1.4: resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -2338,71 +1757,18 @@ inherits@2: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" integrity sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0= -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-builtin-module@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-builtin-module/-/is-builtin-module-3.1.0.tgz#6fdb24313b1c03b75f8b9711c0feb8c30b903b00" - integrity sha512-OV7JjAgOTfAFJmHZLvpSTb4qi0nIILDV1gWPYDnDJUTNFM5aGlRAhk4QcT8i7TuAleeEV5Fdkqn3t4mS+Q11fg== - dependencies: - builtin-modules "^3.0.0" - -is-callable@^1.1.4, is-callable@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" - integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== - -is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-core-module@^2.1.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.2.0.tgz#97037ef3d52224d85163f5597b2b63d9afed981a" - integrity sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ== - dependencies: - has "^1.0.3" - -is-core-module@^2.8.0, is-core-module@^2.9.0: +is-core-module@^2.9.0: version "2.9.0" resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== dependencies: has "^1.0.3" -is-date-object@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" - integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g== - is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" @@ -2432,23 +1798,6 @@ is-glob@^4.0.3: dependencies: is-extglob "^2.1.1" -is-negative-zero@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.1.tgz#3de746c18dda2319241a53675908d8f766f11c24" - integrity sha512-2z6JzQvZRa9A2Y7xC6dQQm4FSTSTNWjKIYYTt4246eMTJmIo0Q+ZyOsU66X8lxK1AbB92dFeglPLrhwpeRKO6w== - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -2459,71 +1808,16 @@ is-potential-custom-element-name@^1.0.1: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== -is-regex@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" - integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== - dependencies: - has-symbols "^1.0.1" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== -is-string@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" - integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== - -is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937" - integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ== - dependencies: - has-symbols "^1.0.1" - -is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - isexe@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -2991,7 +2285,12 @@ jest@^27.0.6: import-local "^3.0.2" jest-cli "^27.5.1" -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: +js-sdsl@^4.1.4: + version "4.1.5" + resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" + integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== + +js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== @@ -3069,50 +2368,15 @@ json5@2.x, json5@^2.2.1: resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -"jsx-ast-utils@^2.4.1 || ^3.0.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82" - integrity sha512-EIsmt3O3ljsU6sot/J4E1zDRxfBNrhjyf/OKjlydwgEimQuznlM4Wv7U+ueONJMyEn1WRE0K8dhi3dVAXYT24Q== - dependencies: - array-includes "^3.1.2" - object.assign "^4.1.2" - -jsx-ast-utils@^3.2.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.2.tgz#afe5efe4332cd3515c065072bd4d6b0aa22152bd" - integrity sha512-4ZCADZHRkno244xlNnn4AOG6sRQ7iBZ5BbgZ4vW4y5IZw7cVUD1PPeblm1xx/nfmMxPdt/LHsXZW8z/j58+l9Q== - dependencies: - array-includes "^3.1.5" - object.assign "^4.1.2" - kleur@^3.0.3: version "3.0.3" resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== -language-subtag-registry@~0.3.2: - version "0.3.21" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" - integrity sha512-L0IqwlIXjilBVVYKFT37X9Ih11Um5NEl9cbJIuU/SwP/zEEAbBPOnEeeuxVMf45ydWQRDQN3Nqc96OgbH1K+Pg== - -language-tags@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha1-0yHbxNowuovzAk4ED6XBRmH5GTo= - dependencies: - language-subtag-registry "~0.3.2" - -lemmy-js-client@0.17.0-rc.37: - version "0.17.0-rc.37" - resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.37.tgz#f211171ea478f095d02676b4e69f69aa300cb571" - integrity sha512-Piz8fnfZnCIFm8AG6Nqw0Oh4i0yr0ZmOwp07FogXMjSP4HKZItHDiDFhXR1qBFklIH7aI0wrvqnLzoIW19SITg== +lemmy-js-client@0.17.0-rc.47: + version "0.17.0-rc.47" + resolved "https://registry.yarnpkg.com/lemmy-js-client/-/lemmy-js-client-0.17.0-rc.47.tgz#6094657d2868767532c0e837a31f9af6273b345b" + integrity sha512-Pc1wyr6sX4Z6LS75NVu46WAXeci5G80+Y9ZBXEAmgM+OZGuOCLUq8lAHRvTwr7M0pj0gxo5yP+i3qPVmTPf+EA== leven@^3.1.0: version "3.1.0" @@ -3140,14 +2404,6 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA= -locate-path@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e" - integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4= - dependencies: - p-locate "^2.0.0" - path-exists "^3.0.0" - locate-path@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" @@ -3155,6 +2411,13 @@ locate-path@^5.0.0: dependencies: p-locate "^4.1.0" +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + lodash.memoize@4.x: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" @@ -3165,18 +2428,11 @@ lodash.merge@^4.6.2: resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== -lodash@^4.17.19, lodash@^4.17.21, lodash@^4.7.0: +lodash@^4.7.0: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3238,11 +2494,6 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -3250,31 +2501,11 @@ minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" -minimist@^1.2.0: - version "1.2.5" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" - integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== - -minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= - ms@2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -3297,16 +2528,6 @@ node-releases@^2.0.6: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== -normalize-package-data@^2.5.0: - version "2.5.0" - resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" - integrity sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA== - dependencies: - hosted-git-info "^2.1.4" - resolve "^1.10.0" - semver "2 || 3 || 4 || 5" - validate-npm-package-license "^3.0.1" - normalize-path@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" @@ -3324,71 +2545,6 @@ nwsapi@^2.2.0: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM= - -object-inspect@^1.12.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-inspect@^1.8.0, object-inspect@^1.9.0: - version "1.9.0" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" - integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== - -object-keys@^1.0.12, object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.1, object.assign@^4.1.2: - version "4.1.2" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940" - integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.hasown@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.19.5" - -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - once@^1.3.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" @@ -3427,13 +2583,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -p-limit@^1.1.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8" - integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q== - dependencies: - p-try "^1.0.0" - p-limit@^2.2.0: version "2.3.0" resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" @@ -3441,12 +2590,12 @@ p-limit@^2.2.0: dependencies: p-try "^2.0.0" -p-locate@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43" - integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM= +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== dependencies: - p-limit "^1.1.0" + yocto-queue "^0.1.0" p-locate@^4.1.0: version "4.1.0" @@ -3455,10 +2604,12 @@ p-locate@^4.1.0: dependencies: p-limit "^2.2.0" -p-try@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3" - integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M= +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" p-try@^2.0.0: version "2.2.0" @@ -3472,16 +2623,6 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" -parse-json@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.1.0.tgz#f96088cdf24a8faa9aea9a009f2d9d942c999646" - integrity sha512-+mi/lmVVNKFNVyLXV31ERiy2CY5E1/F6QtJFEzoChPRwwngMNXRDQ9GJ5WdE2Z2P4AujsOi0/+2qHID68KwfIQ== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - parse-json@^5.2.0: version "5.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" @@ -3497,11 +2638,6 @@ parse5@6.0.1: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -path-exists@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515" - integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU= - path-exists@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" @@ -3517,7 +2653,7 @@ path-key@^3.0.0, path-key@^3.1.0: resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== -path-parse@^1.0.6, path-parse@^1.0.7: +path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -3549,11 +2685,6 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" -pluralize@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-8.0.0.tgz#1a6fa16a38d12a1901e0320fa017051c539ce3b1" - integrity sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA== - prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -3603,15 +2734,6 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types@^15.7.2: - version "15.7.2" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" - integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.8.1" - psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -3622,69 +2744,16 @@ punycode@^2.1.0, punycode@^2.1.1: resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== -react-is@^16.8.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - react-is@^17.0.1: version "17.0.2" resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== -read-pkg-up@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507" - integrity sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg== - dependencies: - find-up "^4.1.0" - read-pkg "^5.2.0" - type-fest "^0.8.1" - -read-pkg@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-5.2.0.tgz#7bf295438ca5a33e56cd30e053b34ee7250c93cc" - integrity sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg== - dependencies: - "@types/normalize-package-data" "^2.4.0" - normalize-package-data "^2.5.0" - parse-json "^5.0.0" - type-fest "^0.6.0" - reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== -regenerator-runtime@^0.13.4: - version "0.13.7" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz#cac2dacc8a1ea675feaabaeb8ae833898ae46f55" - integrity sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew== - -regexp-tree@^0.1.24: - version "0.1.24" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.24.tgz#3d6fa238450a4d66e5bc9c4c14bb720e2196829d" - integrity sha512-s2aEVuLhvnVJW6s/iPgEGK6R+/xngd2jNQ+xy4bXNDKxZKJH6jpPHY6kVeVv1IeLCHgswRj+Kl3ELaDjG6V1iw== - -regexp-tree@~0.1.1: - version "0.1.21" - resolved "https://registry.yarnpkg.com/regexp-tree/-/regexp-tree-0.1.21.tgz#55e2246b7f7d36f1b461490942fa780299c400d7" - integrity sha512-kUUXjX4AnqnR8KRTCrayAo9PzYMRKmVoGgaz2tBuz0MF3g1ZbGebmtW0yFHfFK9CmBjQKeYIgoL22pFLBJY7sw== - -regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2" - integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q== - regexpp@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" @@ -3717,14 +2786,6 @@ resolve.exports@^1.1.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== -resolve@^1.10.0, resolve@^1.10.1, resolve@^1.12.0: - version "1.19.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.19.0.tgz#1af5bf630409734a067cae29318aac7fa29a267c" - integrity sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg== - dependencies: - is-core-module "^2.1.0" - path-parse "^1.0.6" - resolve@^1.20.0: version "1.22.1" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" @@ -3734,15 +2795,6 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" -resolve@^2.0.0-next.3: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - reusify@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" @@ -3765,13 +2817,6 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-regex@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-2.1.1.tgz#f7128f00d056e2fe5c11e81a1324dd974aadced2" - integrity sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A== - dependencies: - regexp-tree "~0.1.1" - "safer-buffer@>= 2.1.2 < 3": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -3784,19 +2829,14 @@ saxes@^5.0.1: dependencies: xmlchars "^2.2.0" -"semver@2 || 3 || 4 || 5": - version "5.7.1" - resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" - integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== - -semver@7.x, semver@^7.3.2, semver@^7.3.5, semver@^7.3.7: +semver@7.x, semver@^7.3.2, semver@^7.3.7: version "7.3.7" resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== dependencies: lru-cache "^6.0.0" -semver@^6.0.0, semver@^6.1.0, semver@^6.3.0: +semver@^6.0.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -3813,15 +2853,6 @@ shebang-regex@^3.0.0: resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - signal-exit@^3.0.2, signal-exit@^3.0.3: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" @@ -3855,32 +2886,6 @@ source-map@^0.7.3: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== -spdx-correct@^3.0.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.1.tgz#dece81ac9c1e6713e5f7d1b6f17d468fa53d89a9" - integrity sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w== - dependencies: - spdx-expression-parse "^3.0.0" - spdx-license-ids "^3.0.0" - -spdx-exceptions@^2.1.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz#3f28ce1a77a00372683eade4a433183527a2163d" - integrity sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A== - -spdx-expression-parse@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz#cf70f50482eefdc98e3ce0a6833e4a53ceeba679" - integrity sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q== - dependencies: - spdx-exceptions "^2.1.0" - spdx-license-ids "^3.0.0" - -spdx-license-ids@^3.0.0: - version "3.0.7" - resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.7.tgz#e9c18a410e5ed7e12442a549fbd8afa767038d65" - integrity sha512-U+MTEOO0AiDzxwFvoa4JVnMV6mZlJKk2sBLt90s7G0Gd0Mlknc7kxEn3nuDPNZRta7O2uy8oLcZLVT+4sqNZHQ== - sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -3910,54 +2915,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string.prototype.matchall@^4.0.6: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" - side-channel "^1.0.4" - -string.prototype.trimend@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.3.tgz#a22bd53cca5c7cf44d7c9d5c732118873d6cd18b" - integrity sha512-ayH0pB+uf0U28CtjlLvL7NaohvR1amUvVZk+y3DYb0Ey2PUV5zPkkKy9+U1ndVEIXO8hNg18eIv9Jntbii+dKw== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" - integrity sha512-oBIBUy5lea5tt0ovtOFiEQaBkoBBkyJhZXzJYrSmDo5IUUqbOPvVezuRs/agBIdZ2p2Eo1FD6bD9USyBLfl3xg== - dependencies: - call-bind "^1.0.0" - define-properties "^1.1.3" - -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -3965,11 +2922,6 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM= - strip-bom@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" @@ -3980,13 +2932,6 @@ strip-final-newline@^2.0.0: resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" @@ -4110,16 +3055,6 @@ ts-jest@^27.0.3: semver "7.x" yargs-parser "20.x" -tsconfig-paths@^3.12.0: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" @@ -4161,16 +3096,6 @@ type-fest@^0.21.3: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== -type-fest@^0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b" - integrity sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg== - -type-fest@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d" - integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA== - typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -4178,20 +3103,10 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" -typescript@^4.6.4: - version "4.7.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.4.tgz#1a88596d1cf47d59507a1bcdfb5b9dfe4d488235" - integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" +typescript@^4.8.4: + version "4.8.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" + integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== universalify@^0.1.2: version "0.1.2" @@ -4213,11 +3128,6 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -v8-compile-cache@^2.0.3: - version "2.2.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.2.0.tgz#9471efa3ef9128d2f7c6a7ca39c4dd6b5055b132" - integrity sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q== - v8-to-istanbul@^8.1.0: version "8.1.1" resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz#77b752fd3975e31bbcef938f85e9bd1c7a8d60ed" @@ -4227,14 +3137,6 @@ v8-to-istanbul@^8.1.0: convert-source-map "^1.6.0" source-map "^0.7.3" -validate-npm-package-license@^3.0.1: - version "3.0.4" - resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" - integrity sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew== - dependencies: - spdx-correct "^3.0.0" - spdx-expression-parse "^3.0.0" - w3c-hr-time@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" @@ -4300,17 +3202,6 @@ whatwg-url@^8.0.0, whatwg-url@^8.5.0: tr46 "^2.1.0" webidl-conversions "^6.1.0" -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -4389,3 +3280,8 @@ yargs@^16.2.0: string-width "^4.2.0" y18n "^5.0.5" yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/config/config.hjson b/config/config.hjson index 8d2ae84c..252fca25 100644 --- a/config/config.hjson +++ b/config/config.hjson @@ -2,11 +2,4 @@ # https://join-lemmy.org/docs/en/administration/configuration.html { hostname: lemmy-alpha - federation: { - enabled: true - } - slur_filter: - ''' - (fag(g|got|tard)?\b|cock\s?sucker(s|ing)?|ni((g{2,}|q)+|[gq]{2,})[e3r]+(s|z)?|mudslime?s?|kikes?|\bspi(c|k)s?\b|\bchinks?|gooks?|bitch(es|ing|y)?|whor(es?|ing)|\btr(a|@)nn?(y|ies?)|\b(b|re|r)tard(ed)?s?) - ''' } diff --git a/config/defaults.hjson b/config/defaults.hjson index b4e1e3df..e9211778 100644 --- a/config/defaults.hjson +++ b/config/defaults.hjson @@ -14,64 +14,7 @@ # Maximum number of active sql connections pool_size: 5 } - # rate limits for various user actions, by user ip - rate_limit: { - # Maximum number of messages created in interval - message: 180 - # Interval length for message limit, in seconds - message_per_second: 60 - # Maximum number of posts created in interval - post: 6 - # Interval length for post limit, in seconds - post_per_second: 600 - # Maximum number of registrations in interval - register: 3 - # Interval length for registration limit, in seconds - register_per_second: 3600 - # Maximum number of image uploads in interval - image: 6 - # Interval length for image uploads, in seconds - image_per_second: 3600 - # Maximum number of comments created in interval - comment: 6 - # Interval length for comment limit, in seconds - comment_per_second: 600 - search: 60 - # Interval length for search limit, in seconds - search_per_second: 600 - } # Settings related to activitypub federation - federation: { - # Whether to enable activitypub federation. - enabled: false - # Allows and blocks are described here: - # https://join-lemmy.org/docs/en/administration/federation_getting_started.html - # - # list of instances with which federation is allowed - allowed_instances: [ - instance1.tld - instance2.tld - /* ... */ - ] - # Instances which we never federate anything with (but previously federated objects are unaffected) - blocked_instances: [ - string - /* ... */ - ] - # If true, only federate with instances on the allowlist and block everything else. If false - # use allowlist only for remote communities, and posts/comments in local communities - # (meaning remote communities will show content from arbitrary instances). - strict_allowlist: true - # Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object fetch through the search). - http_fetch_retry_limit: 25 - # Number of workers for sending outgoing activities. Search logs for Activity queue stats to - # see information. If running number is consistently close to the worker_count, you should - # increase it. - worker_count: 64 - # Use federation debug mode. Allows connecting to http and localhost urls. Also sends outgoing - # activities synchronously for easier testing. Do not use in production. - debug: false - } # Pictrs image server configuration. pictrs: { # Address where pictrs is available (for image hosting) @@ -79,12 +22,6 @@ # Set a custom pictrs API key. ( Required for deleting images ) api_key: "string" } - captcha: { - # Whether captcha is required for signup - enabled: false - # Can be easy, medium, or hard - difficulty: "medium" - } # Email sending configuration. All options except login/password are mandatory email: { # Hostname and port of the smtp server @@ -117,8 +54,4 @@ port: 8536 # Whether the site is available over TLS. Needs to be true for federation to work. tls_enabled: true - # A regex list of slurs to block / hide - slur_filter: "(\bThis\b)|(\bis\b)|(\bsample\b)" - # Maximum length of local community and user names - actor_name_max_length: 20 } diff --git a/crates/api/src/comment/like.rs b/crates/api/src/comment/like.rs index 4e527dd0..754072ee 100644 --- a/crates/api/src/comment/like.rs +++ b/crates/api/src/comment/like.rs @@ -16,6 +16,7 @@ use lemmy_db_schema::{ source::{ comment::{CommentLike, CommentLikeForm}, comment_reply::CommentReply, + local_site::LocalSite, }, traits::Likeable, }; @@ -35,13 +36,14 @@ impl Perform for CreateCommentLike { websocket_id: Option, ) -> Result { let data: &CreateCommentLike = self; + let local_site = blocking(context.pool(), LocalSite::read).await??; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; let mut recipient_ids = Vec::::new(); // Don't do a downvote if site has downvotes disabled - check_downvotes_enabled(data.score, context.pool()).await?; + check_downvotes_enabled(data.score, &local_site)?; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { diff --git a/crates/api/src/comment_report/create.rs b/crates/api/src/comment_report/create.rs index 6d21f066..5a93fa02 100644 --- a/crates/api/src/comment_report/create.rs +++ b/crates/api/src/comment_report/create.rs @@ -7,7 +7,10 @@ use lemmy_api_common::{ }; use lemmy_apub::protocol::activities::community::report::Report; use lemmy_db_schema::{ - source::comment_report::{CommentReport, CommentReportForm}, + source::{ + comment_report::{CommentReport, CommentReportForm}, + local_site::LocalSite, + }, traits::Reportable, }; use lemmy_db_views::structs::{CommentReportView, CommentView}; @@ -28,9 +31,10 @@ impl Perform for CreateCommentReport { let data: &CreateCommentReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let reason = self.reason.trim(); - check_report_reason(reason, context)?; + check_report_reason(reason, &local_site)?; let person_id = local_user_view.person.id; let comment_id = data.comment_id; diff --git a/crates/api/src/community/hide.rs b/crates/api/src/community/hide.rs index a038e631..278d492d 100644 --- a/crates/api/src/community/hide.rs +++ b/crates/api/src/community/hide.rs @@ -7,11 +7,10 @@ use lemmy_api_common::{ use lemmy_apub::protocol::activities::community::update::UpdateCommunity; use lemmy_db_schema::{ source::{ - community::{Community, CommunityForm}, + community::{Community, CommunityUpdateForm}, moderator::{ModHideCommunity, ModHideCommunityForm}, }, traits::Crud, - utils::naive_now, }; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; @@ -33,20 +32,9 @@ impl Perform for HideCommunity { get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; is_admin(&local_user_view)?; - let community_id = data.community_id; - let read_community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let community_form = CommunityForm { - name: read_community.name, - title: read_community.title, - description: Some(read_community.description.to_owned()), - hidden: Some(data.hidden), - updated: Some(naive_now()), - ..CommunityForm::default() - }; + let community_form = CommunityUpdateForm::builder() + .hidden(Some(data.hidden)) + .build(); let mod_hide_community_form = ModHideCommunityForm { community_id: data.community_id, diff --git a/crates/api/src/lib.rs b/crates/api/src/lib.rs index e3419f38..595ab10f 100644 --- a/crates/api/src/lib.rs +++ b/crates/api/src/lib.rs @@ -7,8 +7,10 @@ use lemmy_api_common::{ post::*, private_message::*, site::*, + utils::local_site_to_slur_regex, websocket::*, }; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_utils::{error::LemmyError, utils::check_slurs, ConnectionId}; use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation}; use serde::Deserialize; @@ -227,8 +229,10 @@ pub(crate) fn captcha_as_wav_base64(captcha: &Captcha) -> String { } /// Check size of report and remove whitespace -pub(crate) fn check_report_reason(reason: &str, context: &LemmyContext) -> Result<(), LemmyError> { - check_slurs(reason, &context.settings().slur_regex())?; +pub(crate) fn check_report_reason(reason: &str, local_site: &LocalSite) -> Result<(), LemmyError> { + let slur_regex = &local_site_to_slur_regex(local_site); + + check_slurs(reason, slur_regex)?; if reason.is_empty() { return Err(LemmyError::from_message("report_reason_required")); } @@ -243,8 +247,9 @@ mod tests { use lemmy_api_common::utils::check_validator_time; use lemmy_db_schema::{ source::{ - local_user::{LocalUser, LocalUserForm}, - person::{Person, PersonForm}, + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm}, secret::Secret, }, traits::Crud, @@ -258,19 +263,20 @@ mod tests { let secret = Secret::init(conn).unwrap(); let settings = &SETTINGS.to_owned(); - let new_person = PersonForm { - name: "Gerry9812".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("Gerry9812".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(inserted_person.id), - password_encrypted: Some("123456".to_string()), - ..LocalUserForm::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .password_encrypted("123456".to_string()) + .build(); let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap(); diff --git a/crates/api/src/local_user/add_admin.rs b/crates/api/src/local_user/add_admin.rs index b44b210b..2561906e 100644 --- a/crates/api/src/local_user/add_admin.rs +++ b/crates/api/src/local_user/add_admin.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ moderator::{ModAdd, ModAddForm}, - person::Person, + person::{Person, PersonUpdateForm}, }, traits::Crud, }; @@ -35,7 +35,11 @@ impl Perform for AddAdmin { let added = data.added; let added_person_id = data.person_id; let added_admin = blocking(context.pool(), move |conn| { - Person::add_admin(conn, added_person_id, added) + Person::update( + conn, + added_person_id, + &PersonUpdateForm::builder().admin(Some(added)).build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; diff --git a/crates/api/src/local_user/ban_person.rs b/crates/api/src/local_user/ban_person.rs index fce9b076..f9dc83e0 100644 --- a/crates/api/src/local_user/ban_person.rs +++ b/crates/api/src/local_user/ban_person.rs @@ -11,11 +11,11 @@ use lemmy_apub::{ use lemmy_db_schema::{ source::{ moderator::{ModBan, ModBanForm}, - person::Person, - site::Site, + person::{Person, PersonUpdateForm}, }, traits::Crud, }; +use lemmy_db_views::structs::SiteView; use lemmy_db_views_actor::structs::PersonViewSafe; use lemmy_utils::{error::LemmyError, utils::naive_from_unix, ConnectionId}; use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperation}; @@ -41,10 +41,18 @@ impl Perform for BanPerson { let banned_person_id = data.person_id; let expires = data.expires.map(naive_from_unix); - let ban_person = move |conn: &mut _| Person::ban_person(conn, banned_person_id, ban, expires); - let person = blocking(context.pool(), ban_person) - .await? - .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; + let person = blocking(context.pool(), move |conn| { + Person::update( + conn, + banned_person_id, + &PersonUpdateForm::builder() + .banned(Some(ban)) + .ban_expires(Some(expires)) + .build(), + ) + }) + .await? + .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_user"))?; // Remove their data if that's desired let remove_data = data.remove_data.unwrap_or(false); @@ -75,7 +83,12 @@ impl Perform for BanPerson { }) .await??; - let site = SiteOrCommunity::Site(blocking(context.pool(), Site::read_local).await??.into()); + let site = SiteOrCommunity::Site( + blocking(context.pool(), SiteView::read_local) + .await?? + .site + .into(), + ); // if the action affects a local user, federate to other instances if person.local { if ban { diff --git a/crates/api/src/local_user/get_captcha.rs b/crates/api/src/local_user/get_captcha.rs index b6355036..1d2511f5 100644 --- a/crates/api/src/local_user/get_captcha.rs +++ b/crates/api/src/local_user/get_captcha.rs @@ -2,8 +2,11 @@ use crate::{captcha_as_wav_base64, Perform}; use actix_web::web::Data; use captcha::{gen, Difficulty}; use chrono::Duration; -use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse}; -use lemmy_db_schema::utils::naive_now; +use lemmy_api_common::{ + person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse}, + utils::blocking, +}; +use lemmy_db_schema::{source::local_site::LocalSite, utils::naive_now}; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::{messages::CaptchaItem, LemmyContext}; @@ -17,13 +20,13 @@ impl Perform for GetCaptcha { context: &Data, _websocket_id: Option, ) -> Result { - let captcha_settings = &context.settings().captcha; + let local_site = blocking(context.pool(), LocalSite::read).await??; - if !captcha_settings.enabled { + if !local_site.captcha_enabled { return Ok(GetCaptchaResponse { ok: None }); } - let captcha = gen(match captcha_settings.difficulty.as_str() { + let captcha = gen(match local_site.captcha_difficulty.as_str() { "easy" => Difficulty::Easy, "hard" => Difficulty::Hard, _ => Difficulty::Medium, diff --git a/crates/api/src/local_user/login.rs b/crates/api/src/local_user/login.rs index 637f0d88..990424c0 100644 --- a/crates/api/src/local_user/login.rs +++ b/crates/api/src/local_user/login.rs @@ -5,7 +5,7 @@ use lemmy_api_common::{ person::{Login, LoginResponse}, utils::{blocking, check_registration_application, check_user_valid}, }; -use lemmy_db_schema::source::site::Site; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::LocalUserView; use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -22,6 +22,8 @@ impl Perform for Login { ) -> Result { let data: &Login = self; + let local_site = blocking(context.pool(), LocalSite::read).await??; + // Fetch that username / email let username_or_email = data.username_or_email.clone(); let local_user_view = blocking(context.pool(), move |conn| { @@ -45,12 +47,11 @@ impl Perform for Login { local_user_view.person.deleted, )?; - let site = blocking(context.pool(), Site::read_local).await??; - if site.require_email_verification && !local_user_view.local_user.email_verified { + if local_site.require_email_verification && !local_user_view.local_user.email_verified { return Err(LemmyError::from_message("email_not_verified")); } - check_registration_application(&site, &local_user_view, context.pool()).await?; + check_registration_application(&local_user_view, &local_site, context.pool()).await?; // Return the jwt Ok(LoginResponse { diff --git a/crates/api/src/local_user/notifications/mark_mention_read.rs b/crates/api/src/local_user/notifications/mark_mention_read.rs index 2179ada0..134b4311 100644 --- a/crates/api/src/local_user/notifications/mark_mention_read.rs +++ b/crates/api/src/local_user/notifications/mark_mention_read.rs @@ -4,7 +4,10 @@ use lemmy_api_common::{ person::{MarkPersonMentionAsRead, PersonMentionResponse}, utils::{blocking, get_local_user_view_from_jwt}, }; -use lemmy_db_schema::{source::person_mention::PersonMention, traits::Crud}; +use lemmy_db_schema::{ + source::person_mention::{PersonMention, PersonMentionUpdateForm}, + traits::Crud, +}; use lemmy_db_views_actor::structs::PersonMentionView; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -34,12 +37,12 @@ impl Perform for MarkPersonMentionAsRead { } let person_mention_id = read_person_mention.id; - let read = data.read; - let update_mention = - move |conn: &mut _| PersonMention::update_read(conn, person_mention_id, read); - blocking(context.pool(), update_mention) - .await? - .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; + let read = Some(data.read); + blocking(context.pool(), move |conn| { + PersonMention::update(conn, person_mention_id, &PersonMentionUpdateForm { read }) + }) + .await? + .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; let person_mention_id = read_person_mention.id; let person_id = local_user_view.person.id; diff --git a/crates/api/src/local_user/notifications/mark_reply_read.rs b/crates/api/src/local_user/notifications/mark_reply_read.rs index 3e502f39..e270d547 100644 --- a/crates/api/src/local_user/notifications/mark_reply_read.rs +++ b/crates/api/src/local_user/notifications/mark_reply_read.rs @@ -4,7 +4,10 @@ use lemmy_api_common::{ person::{CommentReplyResponse, MarkCommentReplyAsRead}, utils::{blocking, get_local_user_view_from_jwt}, }; -use lemmy_db_schema::{source::comment_reply::CommentReply, traits::Crud}; +use lemmy_db_schema::{ + source::comment_reply::{CommentReply, CommentReplyUpdateForm}, + traits::Crud, +}; use lemmy_db_views_actor::structs::CommentReplyView; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -34,11 +37,12 @@ impl Perform for MarkCommentReplyAsRead { } let comment_reply_id = read_comment_reply.id; - let read = data.read; - let update_reply = move |conn: &mut _| CommentReply::update_read(conn, comment_reply_id, read); - blocking(context.pool(), update_reply) - .await? - .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; + let read = Some(data.read); + blocking(context.pool(), move |conn| { + CommentReply::update(conn, comment_reply_id, &CommentReplyUpdateForm { read }) + }) + .await? + .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; let comment_reply_id = read_comment_reply.id; let person_id = local_user_view.person.id; diff --git a/crates/api/src/local_user/save_settings.rs b/crates/api/src/local_user/save_settings.rs index ccb1a340..eac1eef0 100644 --- a/crates/api/src/local_user/save_settings.rs +++ b/crates/api/src/local_user/save_settings.rs @@ -7,12 +7,12 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ actor_language::LocalUserLanguage, - local_user::{LocalUser, LocalUserForm}, - person::{Person, PersonForm}, - site::Site, + local_site::LocalSite, + local_user::{LocalUser, LocalUserUpdateForm}, + person::{Person, PersonUpdateForm}, }, traits::Crud, - utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, + utils::{diesel_option_overwrite, diesel_option_overwrite_to_url}, }; use lemmy_utils::{ claims::Claims, @@ -35,6 +35,7 @@ impl Perform for SaveUserSettings { let data: &SaveUserSettings = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let avatar = diesel_option_overwrite_to_url(&data.avatar)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; @@ -56,8 +57,7 @@ impl Perform for SaveUserSettings { // When the site requires email, make sure email is not Some(None). IE, an overwrite to a None value if let Some(email) = &email { - let site_fut = blocking(context.pool(), Site::read_local); - if email.is_none() && site_fut.await??.require_email_verification { + if email.is_none() && local_site.require_email_verification { return Err(LemmyError::from_message("email_required")); } } @@ -71,7 +71,7 @@ impl Perform for SaveUserSettings { if let Some(Some(display_name)) = &display_name { if !is_valid_display_name( display_name.trim(), - context.settings().actor_name_max_length, + local_site.actor_name_max_length as usize, ) { return Err(LemmyError::from_message("invalid_username")); } @@ -87,31 +87,15 @@ impl Perform for SaveUserSettings { let person_id = local_user_view.person.id; let default_listing_type = data.default_listing_type; let default_sort_type = data.default_sort_type; - let password_encrypted = local_user_view.local_user.password_encrypted; - let public_key = Some(local_user_view.person.public_key); - - let person_form = PersonForm { - name: local_user_view.person.name, - avatar, - banner, - inbox_url: None, - display_name, - published: None, - updated: Some(naive_now()), - banned: None, - deleted: None, - actor_id: None, - bio, - local: None, - admin: None, - private_key: None, - public_key, - last_refreshed_at: None, - shared_inbox_url: None, - matrix_user_id, - bot_account, - ban_expires: None, - }; + + let person_form = PersonUpdateForm::builder() + .display_name(display_name) + .bio(bio) + .matrix_user_id(matrix_user_id) + .bot_account(bot_account) + .avatar(avatar) + .banner(banner) + .build(); blocking(context.pool(), move |conn| { Person::update(conn, person_id, &person_form) @@ -126,24 +110,20 @@ impl Perform for SaveUserSettings { .await??; } - let local_user_form = LocalUserForm { - person_id: Some(person_id), - email, - password_encrypted: Some(password_encrypted), - show_nsfw: data.show_nsfw, - show_bot_accounts: data.show_bot_accounts, - show_scores: data.show_scores, - theme: data.theme.to_owned(), - default_sort_type, - default_listing_type, - interface_language: data.interface_language.to_owned(), - show_avatars: data.show_avatars, - show_read_posts: data.show_read_posts, - show_new_post_notifs: data.show_new_post_notifs, - send_notifications_to_email: data.send_notifications_to_email, - email_verified: None, - accepted_application: None, - }; + let local_user_form = LocalUserUpdateForm::builder() + .email(email) + .show_avatars(data.show_avatars) + .show_read_posts(data.show_read_posts) + .show_new_post_notifs(data.show_new_post_notifs) + .send_notifications_to_email(data.send_notifications_to_email) + .show_nsfw(data.show_nsfw) + .show_bot_accounts(data.show_bot_accounts) + .show_scores(data.show_scores) + .default_sort_type(default_sort_type) + .default_listing_type(default_listing_type) + .theme(data.theme.to_owned()) + .interface_language(data.interface_language.to_owned()) + .build(); let local_user_res = blocking(context.pool(), move |conn| { LocalUser::update(conn, local_user_id, &local_user_form) diff --git a/crates/api/src/local_user/verify_email.rs b/crates/api/src/local_user/verify_email.rs index 63a99891..e4f43af1 100644 --- a/crates/api/src/local_user/verify_email.rs +++ b/crates/api/src/local_user/verify_email.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ source::{ email_verification::EmailVerification, - local_user::{LocalUser, LocalUserForm}, + local_user::{LocalUser, LocalUserUpdateForm}, }, traits::Crud, }; @@ -31,13 +31,12 @@ impl Perform for VerifyEmail { .await? .map_err(|e| LemmyError::from_error_message(e, "token_not_found"))?; - let form = LocalUserForm { + let form = LocalUserUpdateForm::builder() // necessary in case this is a new signup - email_verified: Some(true), + .email_verified(Some(true)) // necessary in case email of an existing user was changed - email: Some(Some(verification.email)), - ..LocalUserForm::default() - }; + .email(Some(Some(verification.email))) + .build(); let local_user_id = verification.local_user_id; blocking(context.pool(), move |conn| { LocalUser::update(conn, local_user_id, &form) diff --git a/crates/api/src/post/like.rs b/crates/api/src/post/like.rs index ef954675..ee29a021 100644 --- a/crates/api/src/post/like.rs +++ b/crates/api/src/post/like.rs @@ -20,7 +20,10 @@ use lemmy_apub::{ }, }; use lemmy_db_schema::{ - source::post::{Post, PostLike, PostLikeForm}, + source::{ + local_site::LocalSite, + post::{Post, PostLike, PostLikeForm}, + }, traits::{Crud, Likeable}, }; use lemmy_utils::{error::LemmyError, ConnectionId}; @@ -39,9 +42,10 @@ impl Perform for CreatePostLike { let data: &CreatePostLike = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; // Don't do a downvote if site has downvotes disabled - check_downvotes_enabled(data.score, context.pool()).await?; + check_downvotes_enabled(data.score, &local_site)?; // Check for a community ban let post_id = data.post_id; diff --git a/crates/api/src/post/lock.rs b/crates/api/src/post/lock.rs index 1e11117f..5ce26c5e 100644 --- a/crates/api/src/post/lock.rs +++ b/crates/api/src/post/lock.rs @@ -17,7 +17,7 @@ use lemmy_apub::{ use lemmy_db_schema::{ source::{ moderator::{ModLockPost, ModLockPostForm}, - post::Post, + post::{Post, PostUpdateForm}, }, traits::Crud, }; @@ -61,7 +61,11 @@ impl Perform for LockPost { let post_id = data.post_id; let locked = data.locked; let updated_post: ApubPost = blocking(context.pool(), move |conn| { - Post::update_locked(conn, post_id, locked) + Post::update( + conn, + post_id, + &PostUpdateForm::builder().locked(Some(locked)).build(), + ) }) .await?? .into(); diff --git a/crates/api/src/post/sticky.rs b/crates/api/src/post/sticky.rs index 952eed6f..629de9d6 100644 --- a/crates/api/src/post/sticky.rs +++ b/crates/api/src/post/sticky.rs @@ -17,7 +17,7 @@ use lemmy_apub::{ use lemmy_db_schema::{ source::{ moderator::{ModStickyPost, ModStickyPostForm}, - post::Post, + post::{Post, PostUpdateForm}, }, traits::Crud, }; @@ -61,7 +61,11 @@ impl Perform for StickyPost { let post_id = data.post_id; let stickied = data.stickied; let updated_post: ApubPost = blocking(context.pool(), move |conn| { - Post::update_stickied(conn, post_id, stickied) + Post::update( + conn, + post_id, + &PostUpdateForm::builder().stickied(Some(stickied)).build(), + ) }) .await?? .into(); diff --git a/crates/api/src/post_report/create.rs b/crates/api/src/post_report/create.rs index 6843bcd3..24379f46 100644 --- a/crates/api/src/post_report/create.rs +++ b/crates/api/src/post_report/create.rs @@ -7,7 +7,10 @@ use lemmy_api_common::{ }; use lemmy_apub::protocol::activities::community::report::Report; use lemmy_db_schema::{ - source::post_report::{PostReport, PostReportForm}, + source::{ + local_site::LocalSite, + post_report::{PostReport, PostReportForm}, + }, traits::Reportable, }; use lemmy_db_views::structs::{PostReportView, PostView}; @@ -28,9 +31,10 @@ impl Perform for CreatePostReport { let data: &CreatePostReport = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let reason = self.reason.trim(); - check_report_reason(reason, context)?; + check_report_reason(reason, &local_site)?; let person_id = local_user_view.person.id; let post_id = data.post_id; diff --git a/crates/api/src/private_message/mark_read.rs b/crates/api/src/private_message/mark_read.rs index cdf38b80..6a5737ce 100644 --- a/crates/api/src/private_message/mark_read.rs +++ b/crates/api/src/private_message/mark_read.rs @@ -4,7 +4,10 @@ use lemmy_api_common::{ private_message::{MarkPrivateMessageAsRead, PrivateMessageResponse}, utils::{blocking, get_local_user_view_from_jwt}, }; -use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud}; +use lemmy_db_schema::{ + source::private_message::{PrivateMessage, PrivateMessageUpdateForm}, + traits::Crud, +}; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperation}; @@ -36,7 +39,11 @@ impl Perform for MarkPrivateMessageAsRead { let private_message_id = data.private_message_id; let read = data.read; blocking(context.pool(), move |conn| { - PrivateMessage::update_read(conn, private_message_id, read) + PrivateMessage::update( + conn, + private_message_id, + &PrivateMessageUpdateForm::builder().read(Some(read)).build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; diff --git a/crates/api/src/private_message_report/create.rs b/crates/api/src/private_message_report/create.rs index ee78af80..8e661b79 100644 --- a/crates/api/src/private_message_report/create.rs +++ b/crates/api/src/private_message_report/create.rs @@ -7,6 +7,7 @@ use lemmy_api_common::{ use lemmy_db_schema::{ newtypes::CommunityId, source::{ + local_site::LocalSite, private_message::PrivateMessage, private_message_report::{PrivateMessageReport, PrivateMessageReportForm}, }, @@ -28,9 +29,10 @@ impl Perform for CreatePrivateMessageReport { ) -> Result { let local_user_view = get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let reason = self.reason.trim(); - check_report_reason(reason, context)?; + check_report_reason(reason, &local_site)?; let person_id = local_user_view.person.id; let private_message_id = self.private_message_id; diff --git a/crates/api/src/site/leave_admin.rs b/crates/api/src/site/leave_admin.rs index b5754c9b..9ba48f65 100644 --- a/crates/api/src/site/leave_admin.rs +++ b/crates/api/src/site/leave_admin.rs @@ -2,14 +2,14 @@ use crate::Perform; use actix_web::web::Data; use lemmy_api_common::{ site::{GetSiteResponse, LeaveAdmin}, - utils::{blocking, build_federated_instances, get_local_user_view_from_jwt, is_admin}, + utils::{blocking, get_local_user_view_from_jwt, is_admin}, }; use lemmy_db_schema::{ source::{ actor_language::SiteLanguage, language::Language, moderator::{ModAdd, ModAddForm}, - person::Person, + person::{Person, PersonUpdateForm}, }, traits::Crud, }; @@ -42,7 +42,11 @@ impl Perform for LeaveAdmin { let person_id = local_user_view.person.id; blocking(context.pool(), move |conn| { - Person::leave_admin(conn, person_id) + Person::update( + conn, + person_id, + &PersonUpdateForm::builder().admin(Some(false)).build(), + ) }) .await??; @@ -59,18 +63,16 @@ impl Perform for LeaveAdmin { let site_view = blocking(context.pool(), SiteView::read_local).await??; let admins = blocking(context.pool(), PersonViewSafe::admins).await??; - let federated_instances = build_federated_instances(context.pool(), context.settings()).await?; - let all_languages = blocking(context.pool(), Language::read_all).await??; let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??; Ok(GetSiteResponse { - site_view: Some(site_view), + site_view, admins, online: 0, version: version::VERSION.to_string(), my_user: None, - federated_instances, + federated_instances: None, all_languages, discussion_languages, }) diff --git a/crates/api/src/site/mod_log.rs b/crates/api/src/site/mod_log.rs index 56cc9fdf..6241c509 100644 --- a/crates/api/src/site/mod_log.rs +++ b/crates/api/src/site/mod_log.rs @@ -12,7 +12,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ newtypes::{CommunityId, PersonId}, - source::site::Site, + source::local_site::LocalSite, ModlogActionType, }; use lemmy_db_views_moderator::structs::{ @@ -52,13 +52,13 @@ impl Perform for GetModlog { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let type_ = data.type_.unwrap_or(All); let community_id = data.community_id; - let site = blocking(context.pool(), Site::read_local).await??; let (local_person_id, is_admin) = match local_user_view { Some(s) => (s.person.id, is_admin(&s).is_ok()), None => (PersonId(-1), false), @@ -71,7 +71,7 @@ impl Perform for GetModlog { && is_mod_or_admin(context.pool(), local_person_id, community_id_value) .await .is_ok(); - let hide_modlog_names = site.hide_modlog_mod_names && !is_mod_of_community && !is_admin; + let hide_modlog_names = local_site.hide_modlog_mod_names && !is_mod_of_community && !is_admin; let mod_person_id = if hide_modlog_names { None diff --git a/crates/api/src/site/registration_applications/approve.rs b/crates/api/src/site/registration_applications/approve.rs index 5ebf812d..129e92b7 100644 --- a/crates/api/src/site/registration_applications/approve.rs +++ b/crates/api/src/site/registration_applications/approve.rs @@ -6,8 +6,8 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ source::{ - local_user::{LocalUser, LocalUserForm}, - registration_application::{RegistrationApplication, RegistrationApplicationForm}, + local_user::{LocalUser, LocalUserUpdateForm}, + registration_application::{RegistrationApplication, RegistrationApplicationUpdateForm}, }, traits::Crud, utils::diesel_option_overwrite, @@ -36,10 +36,9 @@ impl Perform for ApproveRegistrationApplication { // Update the registration with reason, admin_id let deny_reason = diesel_option_overwrite(&data.deny_reason); - let app_form = RegistrationApplicationForm { - admin_id: Some(local_user_view.person.id), + let app_form = RegistrationApplicationUpdateForm { + admin_id: Some(Some(local_user_view.person.id)), deny_reason, - ..RegistrationApplicationForm::default() }; let registration_application = blocking(context.pool(), move |conn| { @@ -48,10 +47,9 @@ impl Perform for ApproveRegistrationApplication { .await??; // Update the local_user row - let local_user_form = LocalUserForm { - accepted_application: Some(data.approve), - ..LocalUserForm::default() - }; + let local_user_form = LocalUserUpdateForm::builder() + .accepted_application(Some(data.approve)) + .build(); let approved_user_id = registration_application.local_user_id; blocking(context.pool(), move |conn| { diff --git a/crates/api/src/site/registration_applications/list.rs b/crates/api/src/site/registration_applications/list.rs index ea8f6077..9329151b 100644 --- a/crates/api/src/site/registration_applications/list.rs +++ b/crates/api/src/site/registration_applications/list.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ site::{ListRegistrationApplications, ListRegistrationApplicationsResponse}, utils::{blocking, get_local_user_view_from_jwt, is_admin}, }; -use lemmy_db_schema::source::site::Site; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::registration_application_view::RegistrationApplicationQuery; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -22,14 +22,13 @@ impl Perform for ListRegistrationApplications { let data = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; // Make sure user is an admin is_admin(&local_user_view)?; let unread_only = data.unread_only; - let verified_email_only = blocking(context.pool(), Site::read_local) - .await?? - .require_email_verification; + let verified_email_only = local_site.require_email_verification; let page = data.page; let limit = data.limit; diff --git a/crates/api/src/site/registration_applications/unread_count.rs b/crates/api/src/site/registration_applications/unread_count.rs index 0fe2934c..32db67bb 100644 --- a/crates/api/src/site/registration_applications/unread_count.rs +++ b/crates/api/src/site/registration_applications/unread_count.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ site::{GetUnreadRegistrationApplicationCount, GetUnreadRegistrationApplicationCountResponse}, utils::{blocking, get_local_user_view_from_jwt, is_admin}, }; -use lemmy_db_schema::source::site::Site; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::RegistrationApplicationView; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -21,13 +21,12 @@ impl Perform for GetUnreadRegistrationApplicationCount { let data = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; // Only let admins do this is_admin(&local_user_view)?; - let verified_email_only = blocking(context.pool(), Site::read_local) - .await?? - .require_email_verification; + let verified_email_only = local_site.require_email_verification; let registration_applications = blocking(context.pool(), move |conn| { RegistrationApplicationView::get_unread_count(conn, verified_email_only) diff --git a/crates/api/src/site/resolve_object.rs b/crates/api/src/site/resolve_object.rs index 57e2a5ce..5d322eec 100644 --- a/crates/api/src/site/resolve_object.rs +++ b/crates/api/src/site/resolve_object.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, }; use lemmy_apub::fetcher::search::{search_query_to_object_id, SearchableObjects}; -use lemmy_db_schema::{newtypes::PersonId, utils::DbPool}; +use lemmy_db_schema::{newtypes::PersonId, source::local_site::LocalSite, utils::DbPool}; use lemmy_db_views::structs::{CommentView, PostView}; use lemmy_db_views_actor::structs::{CommunityView, PersonViewSafe}; use lemmy_utils::{error::LemmyError, ConnectionId}; @@ -25,7 +25,8 @@ impl Perform for ResolveObject { let local_user_view = get_local_user_view_from_jwt_opt(self.auth.as_ref(), context.pool(), context.secret()) .await?; - check_private_instance(&local_user_view, context.pool()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; + check_private_instance(&local_user_view, &local_site)?; let res = search_query_to_object_id(&self.q, local_user_view.is_none(), context) .await diff --git a/crates/api/src/site/search.rs b/crates/api/src/site/search.rs index 9fcde746..6c02f9ac 100644 --- a/crates/api/src/site/search.rs +++ b/crates/api/src/site/search.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; use lemmy_db_schema::{ - source::community::Community, + source::{community::Community, local_site::LocalSite}, traits::DeleteableOrRemoveable, utils::post_to_comment_sort_type, SearchType, @@ -31,7 +31,9 @@ impl Perform for Search { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; - check_private_instance(&local_user_view, context.pool()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; + + check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.as_ref().map(|u| u.person.id); let local_user = local_user_view.map(|l| l.local_user); diff --git a/crates/api_common/Cargo.toml b/crates/api_common/Cargo.toml index e7f6c07c..406517cd 100644 --- a/crates/api_common/Cargo.toml +++ b/crates/api_common/Cargo.toml @@ -35,6 +35,7 @@ percent-encoding = { version = "2.2.0", optional = true } encoding = { version = "0.2.33", optional = true } reqwest-middleware = { version = "0.1.6", optional = true } webpage = { version = "1.4.0", default-features = false, features = ["serde"], optional = true } +regex = "1.6.0" [dev-dependencies] actix-rt = { version = "2.7.0", default-features = false } diff --git a/crates/api_common/src/site.rs b/crates/api_common/src/site.rs index 73c11762..88e1c3ac 100644 --- a/crates/api_common/src/site.rs +++ b/crates/api_common/src/site.rs @@ -128,6 +128,30 @@ pub struct CreateSite { pub application_email_admins: Option, pub auth: Sensitive, pub hide_modlog_mod_names: Option, + pub legal_information: Option, + pub slur_filter_regex: Option, + pub actor_name_max_length: Option, + pub rate_limit_message: Option, + pub rate_limit_message_per_second: Option, + pub rate_limit_post: Option, + pub rate_limit_post_per_second: Option, + pub rate_limit_register: Option, + pub rate_limit_register_per_second: Option, + pub rate_limit_image: Option, + pub rate_limit_image_per_second: Option, + pub rate_limit_comment: Option, + pub rate_limit_comment_per_second: Option, + pub rate_limit_search: Option, + pub rate_limit_search_per_second: Option, + pub federation_enabled: Option, + pub federation_debug: Option, + pub federation_strict_allowlist: Option, + pub federation_http_fetch_retry_limit: Option, + pub federation_worker_count: Option, + pub captcha_enabled: Option, + pub captcha_difficulty: Option, + pub allowed_instances: Option>, + pub blocked_instances: Option>, } #[derive(Debug, Serialize, Deserialize, Clone, Default)] @@ -151,6 +175,29 @@ pub struct EditSite { pub application_email_admins: Option, pub hide_modlog_mod_names: Option, pub discussion_languages: Option>, + pub slur_filter_regex: Option, + pub actor_name_max_length: Option, + pub rate_limit_message: Option, + pub rate_limit_message_per_second: Option, + pub rate_limit_post: Option, + pub rate_limit_post_per_second: Option, + pub rate_limit_register: Option, + pub rate_limit_register_per_second: Option, + pub rate_limit_image: Option, + pub rate_limit_image_per_second: Option, + pub rate_limit_comment: Option, + pub rate_limit_comment_per_second: Option, + pub rate_limit_search: Option, + pub rate_limit_search_per_second: Option, + pub federation_enabled: Option, + pub federation_debug: Option, + pub federation_strict_allowlist: Option, + pub federation_http_fetch_retry_limit: Option, + pub federation_worker_count: Option, + pub captcha_enabled: Option, + pub captcha_difficulty: Option, + pub allowed_instances: Option>, + pub blocked_instances: Option>, pub auth: Sensitive, } @@ -166,7 +213,7 @@ pub struct SiteResponse { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct GetSiteResponse { - pub site_view: Option, // Because the site might not be set up yet + pub site_view: SiteView, pub admins: Vec, pub online: usize, pub version: String, diff --git a/crates/api_common/src/utils.rs b/crates/api_common/src/utils.rs index 2a02b9a7..6b0b011a 100644 --- a/crates/api_common/src/utils.rs +++ b/crates/api_common/src/utils.rs @@ -4,16 +4,18 @@ use lemmy_db_schema::{ impls::person::is_banned, newtypes::{CommunityId, LocalUserId, PersonId, PostId}, source::{ - comment::Comment, - community::Community, + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, email_verification::{EmailVerification, EmailVerificationForm}, + instance::Instance, + local_site::LocalSite, + local_site_rate_limit::LocalSiteRateLimit, password_reset_request::PasswordResetRequest, - person::Person, + person::{Person, PersonUpdateForm}, person_block::PersonBlock, post::{Post, PostRead, PostReadForm}, registration_application::RegistrationApplication, secret::Secret, - site::Site, }, traits::{Crud, Readable}, utils::DbPool, @@ -32,9 +34,11 @@ use lemmy_utils::{ claims::Claims, email::{send_email, translations::Lang}, error::LemmyError, + rate_limit::RateLimitConfig, settings::structs::Settings, - utils::generate_random_string, + utils::{build_slur_regex, generate_random_string}, }; +use regex::Regex; use reqwest_middleware::ClientWithMiddleware; use rosetta_i18n::{Language, LanguageId}; use std::str::FromStr; @@ -265,67 +269,38 @@ pub async fn check_person_block( } #[tracing::instrument(skip_all)] -pub async fn check_downvotes_enabled(score: i16, pool: &DbPool) -> Result<(), LemmyError> { - if score == -1 { - let site = blocking(pool, Site::read_local).await??; - if !site.enable_downvotes { - return Err(LemmyError::from_message("downvotes_disabled")); - } +pub fn check_downvotes_enabled(score: i16, local_site: &LocalSite) -> Result<(), LemmyError> { + if score == -1 && !local_site.enable_downvotes { + return Err(LemmyError::from_message("downvotes_disabled")); } Ok(()) } #[tracing::instrument(skip_all)] -pub async fn check_private_instance( +pub fn check_private_instance( local_user_view: &Option, - pool: &DbPool, + local_site: &LocalSite, ) -> Result<(), LemmyError> { - if local_user_view.is_none() { - let site = blocking(pool, Site::read_local).await?; - - // The site might not be set up yet - if let Ok(site) = site { - if site.private_instance { - return Err(LemmyError::from_message("instance_is_private")); - } - } + if local_user_view.is_none() && local_site.private_instance { + return Err(LemmyError::from_message("instance_is_private")); } Ok(()) } #[tracing::instrument(skip_all)] pub async fn build_federated_instances( + local_site: &LocalSite, pool: &DbPool, - settings: &Settings, ) -> Result, LemmyError> { - let federation_config = &settings.federation; - let hostname = &settings.hostname; - let federation = federation_config.to_owned(); - if federation.enabled { - let distinct_communities = blocking(pool, move |conn| { - Community::distinct_federated_communities(conn) - }) - .await??; - - let allowed = federation.allowed_instances; - let blocked = federation.blocked_instances; + if local_site.federation_enabled { + // TODO I hate that this requires 3 queries + let linked = blocking(pool, Instance::linked).await??; + let allowed = blocking(pool, Instance::allowlist).await??; + let blocked = blocking(pool, Instance::blocklist).await??; - let mut linked = distinct_communities - .iter() - .map(|actor_id| Ok(actor_id.host_str().unwrap_or("").to_string())) - .collect::, LemmyError>>()?; - - if let Some(allowed) = allowed.as_ref() { - linked.extend_from_slice(allowed); - } - - if let Some(blocked) = blocked.as_ref() { - linked.retain(|a| !blocked.contains(a) && !a.eq(hostname)); - } - - // Sort and remove dupes - linked.sort_unstable(); - linked.dedup(); + // These can return empty vectors, so convert them to options + let allowed = (!allowed.is_empty()).then(|| allowed); + let blocked = (!blocked.is_empty()).then(|| blocked); Ok(Some(FederatedInstances { linked, @@ -467,6 +442,37 @@ fn lang_str_to_lang(lang: &str) -> Lang { }) } +pub fn local_site_rate_limit_to_rate_limit_config( + local_site_rate_limit: &LocalSiteRateLimit, +) -> RateLimitConfig { + let l = local_site_rate_limit; + RateLimitConfig { + message: l.message, + message_per_second: l.message_per_second, + post: l.post, + post_per_second: l.post_per_second, + register: l.register, + register_per_second: l.register_per_second, + image: l.image, + image_per_second: l.image_per_second, + comment: l.comment, + comment_per_second: l.comment_per_second, + search: l.search, + search_per_second: l.search_per_second, + } +} + +pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option { + build_slur_regex(local_site.slur_filter_regex.as_deref()) +} + +pub fn local_site_opt_to_slur_regex(local_site: &Option) -> Option { + local_site + .as_ref() + .map(local_site_to_slur_regex) + .unwrap_or(None) +} + pub fn send_application_approved_email( user: &LocalUserView, settings: &Settings, @@ -506,11 +512,11 @@ pub async fn send_new_applicant_email_to_admins( } pub async fn check_registration_application( - site: &Site, local_user_view: &LocalUserView, + local_site: &LocalSite, pool: &DbPool, ) -> Result<(), LemmyError> { - if site.require_application + if local_site.require_application && !local_user_view.local_user.accepted_application && !local_user_view.person.admin { @@ -531,19 +537,13 @@ pub async fn check_registration_application( Ok(()) } -/// TODO this check should be removed after https://github.com/LemmyNet/lemmy/issues/868 is done. -pub async fn check_private_instance_and_federation_enabled( - pool: &DbPool, - settings: &Settings, +pub fn check_private_instance_and_federation_enabled( + local_site: &LocalSite, ) -> Result<(), LemmyError> { - let site_opt = blocking(pool, Site::read_local).await?; - - if let Ok(site) = site_opt { - if site.private_instance && settings.federation.enabled { - return Err(LemmyError::from_message( - "Cannot have both private instance and federation enabled.", - )); - } + if local_site.private_instance && local_site.federation_enabled { + return Err(LemmyError::from_message( + "Cannot have both private instance and federation enabled.", + )); } Ok(()) } @@ -627,7 +627,14 @@ pub async fn remove_user_data( // Update the fields to None blocking(pool, move |conn| { - Person::remove_avatar_and_banner(conn, banned_person_id) + Person::update( + conn, + banned_person_id, + &PersonUpdateForm::builder() + .avatar(Some(None)) + .banner(Some(None)) + .build(), + ) }) .await??; @@ -656,8 +663,12 @@ pub async fn remove_user_data( for first_mod_community in banned_user_first_communities { let community_id = first_mod_community.community.id; - blocking(pool, move |conn: &mut _| { - Community::update_removed(conn, community_id, true) + blocking(pool, move |conn| { + Community::update( + conn, + community_id, + &CommunityUpdateForm::builder().removed(Some(true)).build(), + ) }) .await??; @@ -672,7 +683,14 @@ pub async fn remove_user_data( } // Update the fields to None blocking(pool, move |conn| { - Community::remove_avatar_and_banner(conn, community_id) + Community::update( + conn, + community_id, + &CommunityUpdateForm::builder() + .icon(Some(None)) + .banner(Some(None)) + .build(), + ) }) .await??; } @@ -713,7 +731,11 @@ pub async fn remove_user_data_in_community( for comment_view in &comments { let comment_id = comment_view.comment.id; blocking(pool, move |conn| { - Comment::update_removed(conn, comment_id, true) + Comment::update( + conn, + comment_id, + &CommentUpdateForm::builder().removed(Some(true)).build(), + ) }) .await??; } @@ -761,15 +783,11 @@ pub async fn delete_user_account( Ok(()) } -pub async fn listing_type_with_site_default( +pub fn listing_type_with_site_default( listing_type: Option, - pool: &DbPool, + local_site: &LocalSite, ) -> Result { - Ok(match listing_type { - Some(l) => l, - None => { - let site = blocking(pool, Site::read_local).await??; - ListingType::from_str(&site.default_post_listing_type)? - } - }) + Ok(listing_type.unwrap_or(ListingType::from_str( + &local_site.default_post_listing_type, + )?)) } diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index f3effaad..ef13e54d 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -9,6 +9,7 @@ use lemmy_api_common::{ check_post_deleted_or_removed, get_local_user_view_from_jwt, get_post, + local_site_to_slur_regex, }, }; use lemmy_apub::{ @@ -20,9 +21,10 @@ use lemmy_apub::{ use lemmy_db_schema::{ source::{ actor_language::CommunityLanguage, - comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, - comment_reply::CommentReply, - person_mention::PersonMention, + comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm, CommentUpdateForm}, + comment_reply::{CommentReply, CommentReplyUpdateForm}, + local_site::LocalSite, + person_mention::{PersonMention, PersonMentionUpdateForm}, }, traits::{Crud, Likeable}, }; @@ -50,9 +52,12 @@ impl PerformCrud for CreateComment { let data: &CreateComment = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - let content_slurs_removed = - remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); + let content_slurs_removed = remove_slurs( + &data.content.to_owned(), + &local_site_to_slur_regex(&local_site), + ); // Check for a community ban let post_id = data.post_id; @@ -97,13 +102,12 @@ impl PerformCrud for CreateComment { }) .await??; - let comment_form = CommentForm { - content: content_slurs_removed, - post_id: data.post_id, - creator_id: local_user_view.person.id, - language_id: Some(language_id), - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content(content_slurs_removed.to_owned()) + .post_id(data.post_id) + .creator_id(local_user_view.person.id) + .language_id(Some(language_id)) + .build(); // Create the comment let comment_form2 = comment_form.clone(); @@ -125,14 +129,18 @@ impl PerformCrud for CreateComment { &inserted_comment_id.to_string(), &protocol_and_hostname, )?; - Ok(Comment::update_ap_id(conn, inserted_comment_id, apub_id)?) + Ok(Comment::update( + conn, + inserted_comment_id, + &CommentUpdateForm::builder().ap_id(Some(apub_id)).build(), + )?) }) .await? .map_err(|e| e.with_message("couldnt_create_comment"))?; // Scan the comment for user mentions, add those rows let post_id = post.id; - let mentions = scrape_text_for_mentions(&comment_form.content); + let mentions = scrape_text_for_mentions(&content_slurs_removed); let recipient_ids = send_local_notifs( mentions, &updated_comment, @@ -175,7 +183,7 @@ impl PerformCrud for CreateComment { .await?; if let Ok(reply) = comment_reply { blocking(context.pool(), move |conn| { - CommentReply::update_read(conn, reply.id, true) + CommentReply::update(conn, reply.id, &CommentReplyUpdateForm { read: Some(true) }) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_replies"))?; @@ -189,7 +197,11 @@ impl PerformCrud for CreateComment { .await?; if let Ok(mention) = person_mention { blocking(context.pool(), move |conn| { - PersonMention::update_read(conn, mention.id, true) + PersonMention::update( + conn, + mention.id, + &PersonMentionUpdateForm { read: Some(true) }, + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_person_mentions"))?; diff --git a/crates/api_crud/src/comment/delete.rs b/crates/api_crud/src/comment/delete.rs index a674ed2c..407d11cc 100644 --- a/crates/api_crud/src/comment/delete.rs +++ b/crates/api_crud/src/comment/delete.rs @@ -6,7 +6,11 @@ use lemmy_api_common::{ }; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_db_schema::{ - source::{comment::Comment, community::Community, post::Post}, + source::{ + comment::{Comment, CommentUpdateForm}, + community::Community, + post::Post, + }, traits::Crud, }; use lemmy_db_views::structs::CommentView; @@ -57,7 +61,11 @@ impl PerformCrud for DeleteComment { // Do the delete let deleted = data.deleted; let updated_comment = blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, comment_id, deleted) + Comment::update( + conn, + comment_id, + &CommentUpdateForm::builder().deleted(Some(deleted)).build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; diff --git a/crates/api_crud/src/comment/list.rs b/crates/api_crud/src/comment/list.rs index 92e1771c..9ab7d6d0 100644 --- a/crates/api_crud/src/comment/list.rs +++ b/crates/api_crud/src/comment/list.rs @@ -11,7 +11,7 @@ use lemmy_api_common::{ }; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; use lemmy_db_schema::{ - source::{comment::Comment, community::Community}, + source::{comment::Comment, community::Community, local_site::LocalSite}, traits::{Crud, DeleteableOrRemoveable}, }; use lemmy_db_views::comment_view::CommentQuery; @@ -32,10 +32,11 @@ impl PerformCrud for GetComments { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; - check_private_instance(&local_user_view, context.pool()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; + check_private_instance(&local_user_view, &local_site)?; let community_id = data.community_id; - let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?; + let listing_type = listing_type_with_site_default(data.type_, &local_site)?; let community_actor_id = if let Some(name) = &data.community_name { resolve_actor_identifier::(name, context, true) diff --git a/crates/api_crud/src/comment/read.rs b/crates/api_crud/src/comment/read.rs index f5013c98..a4868c09 100644 --- a/crates/api_crud/src/comment/read.rs +++ b/crates/api_crud/src/comment/read.rs @@ -4,6 +4,7 @@ use lemmy_api_common::{ comment::{CommentResponse, GetComment}, utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, }; +use lemmy_db_schema::source::local_site::LocalSite; use lemmy_db_views::structs::CommentView; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -22,8 +23,9 @@ impl PerformCrud for GetComment { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.map(|u| u.person.id); let id = data.id; diff --git a/crates/api_crud/src/comment/remove.rs b/crates/api_crud/src/comment/remove.rs index fe664d80..3b94fd47 100644 --- a/crates/api_crud/src/comment/remove.rs +++ b/crates/api_crud/src/comment/remove.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_db_schema::{ source::{ - comment::Comment, + comment::{Comment, CommentUpdateForm}, community::Community, moderator::{ModRemoveComment, ModRemoveCommentForm}, post::Post, @@ -60,7 +60,11 @@ impl PerformCrud for RemoveComment { // Do the remove let removed = data.removed; let updated_comment = blocking(context.pool(), move |conn| { - Comment::update_removed(conn, comment_id, removed) + Comment::update( + conn, + comment_id, + &CommentUpdateForm::builder().removed(Some(removed)).build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_comment"))?; diff --git a/crates/api_crud/src/comment/update.rs b/crates/api_crud/src/comment/update.rs index f03ad5f5..f9a18fb3 100644 --- a/crates/api_crud/src/comment/update.rs +++ b/crates/api_crud/src/comment/update.rs @@ -8,6 +8,7 @@ use lemmy_api_common::{ check_post_deleted_or_removed, get_local_user_view_from_jwt, is_mod_or_admin, + local_site_to_slur_regex, }, }; use lemmy_apub::protocol::activities::{ @@ -17,7 +18,8 @@ use lemmy_apub::protocol::activities::{ use lemmy_db_schema::{ source::{ actor_language::CommunityLanguage, - comment::{Comment, CommentForm}, + comment::{Comment, CommentUpdateForm}, + local_site::LocalSite, }, traits::Crud, }; @@ -48,6 +50,7 @@ impl PerformCrud for EditComment { let data: &EditComment = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let comment_id = data.comment_id; let orig_comment = blocking(context.pool(), move |conn| { @@ -90,16 +93,13 @@ impl PerformCrud for EditComment { let content_slurs_removed = data .content .as_ref() - .map(|c| remove_slurs(c, &context.settings().slur_regex())); + .map(|c| remove_slurs(c, &local_site_to_slur_regex(&local_site))); let comment_id = data.comment_id; - let form = CommentForm { - creator_id: orig_comment.comment.creator_id, - post_id: orig_comment.comment.post_id, - content: content_slurs_removed.unwrap_or(orig_comment.comment.content), - distinguished: data.distinguished, - language_id: data.language_id, - ..Default::default() - }; + let form = CommentUpdateForm::builder() + .content(content_slurs_removed) + .distinguished(data.distinguished) + .language_id(data.language_id) + .build(); let updated_comment = blocking(context.pool(), move |conn| { Comment::update(conn, comment_id, &form) }) diff --git a/crates/api_crud/src/community/create.rs b/crates/api_crud/src/community/create.rs index 1f820b9e..63cdc3af 100644 --- a/crates/api_crud/src/community/create.rs +++ b/crates/api_crud/src/community/create.rs @@ -3,7 +3,7 @@ use activitypub_federation::core::{object_id::ObjectId, signatures::generate_act use actix_web::web::Data; use lemmy_api_common::{ community::{CommunityResponse, CreateCommunity}, - utils::{blocking, get_local_user_view_from_jwt, is_admin}, + utils::{blocking, get_local_user_view_from_jwt, is_admin, local_site_to_slur_regex}, }; use lemmy_apub::{ generate_followers_url, @@ -14,20 +14,18 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_schema::{ - source::{ - community::{ - Community, - CommunityFollower, - CommunityFollowerForm, - CommunityForm, - CommunityModerator, - CommunityModeratorForm, - }, - site::Site, + source::community::{ + Community, + CommunityFollower, + CommunityFollowerForm, + CommunityInsertForm, + CommunityModerator, + CommunityModeratorForm, }, traits::{Crud, Followable, Joinable}, - utils::{diesel_option_overwrite, diesel_option_overwrite_to_url}, + utils::diesel_option_overwrite_to_url_create, }; +use lemmy_db_views::structs::SiteView; use lemmy_db_views_actor::structs::CommunityView; use lemmy_utils::{ error::LemmyError, @@ -49,8 +47,9 @@ impl PerformCrud for CreateCommunity { let data: &CreateCommunity = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let site_view = blocking(context.pool(), SiteView::read_local).await??; + let local_site = site_view.local_site; - let local_site = blocking(context.pool(), Site::read_local).await??; if local_site.community_creation_admin_only && is_admin(&local_user_view).is_err() { return Err(LemmyError::from_message( "only_admins_can_create_communities", @@ -58,15 +57,15 @@ impl PerformCrud for CreateCommunity { } // Check to make sure the icon and banners are urls - let icon = diesel_option_overwrite_to_url(&data.icon)?; - let banner = diesel_option_overwrite_to_url(&data.banner)?; - let description = diesel_option_overwrite(&data.description); + let icon = diesel_option_overwrite_to_url_create(&data.icon)?; + let banner = diesel_option_overwrite_to_url_create(&data.banner)?; - check_slurs(&data.name, &context.settings().slur_regex())?; - check_slurs(&data.title, &context.settings().slur_regex())?; - check_slurs_opt(&data.description, &context.settings().slur_regex())?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&data.name, &slur_regex)?; + check_slurs(&data.title, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; - if !is_valid_actor_name(&data.name, context.settings().actor_name_max_length) { + if !is_valid_actor_name(&data.name, local_site.actor_name_max_length as usize) { return Err(LemmyError::from_message("invalid_community_name")); } @@ -85,22 +84,22 @@ impl PerformCrud for CreateCommunity { // When you create a community, make sure the user becomes a moderator and a follower let keypair = generate_actor_keypair()?; - let community_form = CommunityForm { - name: data.name.to_owned(), - title: data.title.to_owned(), - description, - icon, - banner, - nsfw: data.nsfw, - actor_id: Some(community_actor_id.to_owned()), - private_key: Some(Some(keypair.private_key)), - public_key: Some(keypair.public_key), - followers_url: Some(generate_followers_url(&community_actor_id)?), - inbox_url: Some(generate_inbox_url(&community_actor_id)?), - shared_inbox_url: Some(Some(generate_shared_inbox_url(&community_actor_id)?)), - posting_restricted_to_mods: data.posting_restricted_to_mods, - ..CommunityForm::default() - }; + let community_form = CommunityInsertForm::builder() + .name(data.name.to_owned()) + .title(data.title.to_owned()) + .description(data.description.to_owned()) + .icon(icon) + .banner(banner) + .nsfw(data.nsfw) + .actor_id(Some(community_actor_id.to_owned())) + .private_key(Some(keypair.private_key)) + .public_key(keypair.public_key) + .followers_url(Some(generate_followers_url(&community_actor_id)?)) + .inbox_url(Some(generate_inbox_url(&community_actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&community_actor_id)?)) + .posting_restricted_to_mods(data.posting_restricted_to_mods) + .instance_id(site_view.site.instance_id) + .build(); let inserted_community = blocking(context.pool(), move |conn| { Community::create(conn, &community_form) diff --git a/crates/api_crud/src/community/delete.rs b/crates/api_crud/src/community/delete.rs index 441f4581..dff00696 100644 --- a/crates/api_crud/src/community/delete.rs +++ b/crates/api_crud/src/community/delete.rs @@ -5,7 +5,10 @@ use lemmy_api_common::{ utils::{blocking, get_local_user_view_from_jwt}, }; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; -use lemmy_db_schema::source::community::Community; +use lemmy_db_schema::{ + source::community::{Community, CommunityUpdateForm}, + traits::Crud, +}; use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; @@ -40,7 +43,13 @@ impl PerformCrud for DeleteCommunity { let community_id = data.community_id; let deleted = data.deleted; let updated_community = blocking(context.pool(), move |conn| { - Community::update_deleted(conn, community_id, deleted) + Community::update( + conn, + community_id, + &CommunityUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; diff --git a/crates/api_crud/src/community/list.rs b/crates/api_crud/src/community/list.rs index 1aa4585f..dcf5886a 100644 --- a/crates/api_crud/src/community/list.rs +++ b/crates/api_crud/src/community/list.rs @@ -4,7 +4,7 @@ use lemmy_api_common::{ community::{ListCommunities, ListCommunitiesResponse}, utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, }; -use lemmy_db_schema::traits::DeleteableOrRemoveable; +use lemmy_db_schema::{source::local_site::LocalSite, traits::DeleteableOrRemoveable}; use lemmy_db_views_actor::community_view::CommunityQuery; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -23,8 +23,9 @@ impl PerformCrud for ListCommunities { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.to_owned().map(|l| l.person.id); diff --git a/crates/api_crud/src/community/read.rs b/crates/api_crud/src/community/read.rs index 3ea32759..595f3e2b 100644 --- a/crates/api_crud/src/community/read.rs +++ b/crates/api_crud/src/community/read.rs @@ -10,7 +10,12 @@ use lemmy_apub::{ }; use lemmy_db_schema::{ impls::actor_language::default_post_language, - source::{actor_language::CommunityLanguage, community::Community, site::Site}, + source::{ + actor_language::CommunityLanguage, + community::Community, + local_site::LocalSite, + site::Site, + }, traits::DeleteableOrRemoveable, }; use lemmy_db_views_actor::structs::{CommunityModeratorView, CommunityView}; @@ -31,12 +36,13 @@ impl PerformCrud for GetCommunity { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; if data.name.is_none() && data.id.is_none() { return Err(LemmyError::from_message("no_id_given")); } - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.as_ref().map(|u| u.person.id); diff --git a/crates/api_crud/src/community/remove.rs b/crates/api_crud/src/community/remove.rs index 5895a6cb..be486144 100644 --- a/crates/api_crud/src/community/remove.rs +++ b/crates/api_crud/src/community/remove.rs @@ -7,7 +7,7 @@ use lemmy_api_common::{ use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_db_schema::{ source::{ - community::Community, + community::{Community, CommunityUpdateForm}, moderator::{ModRemoveCommunity, ModRemoveCommunityForm}, }, traits::Crud, @@ -36,7 +36,13 @@ impl PerformCrud for RemoveCommunity { let community_id = data.community_id; let removed = data.removed; let updated_community = blocking(context.pool(), move |conn| { - Community::update_removed(conn, community_id, removed) + Community::update( + conn, + community_id, + &CommunityUpdateForm::builder() + .removed(Some(removed)) + .build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_community"))?; diff --git a/crates/api_crud/src/community/update.rs b/crates/api_crud/src/community/update.rs index b7872b38..82ae4cc8 100644 --- a/crates/api_crud/src/community/update.rs +++ b/crates/api_crud/src/community/update.rs @@ -2,17 +2,18 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ community::{CommunityResponse, EditCommunity}, - utils::{blocking, get_local_user_view_from_jwt}, + utils::{blocking, get_local_user_view_from_jwt, local_site_to_slur_regex}, }; use lemmy_apub::protocol::activities::community::update::UpdateCommunity; use lemmy_db_schema::{ newtypes::{LanguageId, PersonId}, source::{ actor_language::{CommunityLanguage, SiteLanguage}, - community::{Community, CommunityForm}, + community::{Community, CommunityUpdateForm}, + local_site::LocalSite, }, traits::Crud, - utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, + utils::{diesel_option_overwrite, diesel_option_overwrite_to_url}, }; use lemmy_db_views_actor::structs::CommunityModeratorView; use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId}; @@ -31,13 +32,15 @@ impl PerformCrud for EditCommunity { let data: &EditCommunity = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let icon = diesel_option_overwrite_to_url(&data.icon)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; let description = diesel_option_overwrite(&data.description); - check_slurs_opt(&data.title, &context.settings().slur_regex())?; - check_slurs_opt(&data.description, &context.settings().slur_regex())?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs_opt(&data.title, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; // Verify its a mod (only mods can edit it) let community_id = data.community_id; @@ -66,22 +69,14 @@ impl PerformCrud for EditCommunity { .await??; } - let read_community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let community_form = CommunityForm { - name: read_community.name, - title: data.title.to_owned().unwrap_or(read_community.title), - description, - icon, - banner, - nsfw: data.nsfw, - posting_restricted_to_mods: data.posting_restricted_to_mods, - updated: Some(naive_now()), - ..CommunityForm::default() - }; + let community_form = CommunityUpdateForm::builder() + .title(data.title.to_owned()) + .description(description) + .icon(icon) + .banner(banner) + .nsfw(data.nsfw) + .posting_restricted_to_mods(data.posting_restricted_to_mods) + .build(); let community_id = data.community_id; let updated_community = blocking(context.pool(), move |conn| { diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 77a32660..2c7f605b 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -9,6 +9,7 @@ use lemmy_api_common::{ check_community_deleted_or_removed, get_local_user_view_from_jwt, honeypot_check, + local_site_to_slur_regex, mark_post_as_read, }, }; @@ -23,10 +24,10 @@ use lemmy_db_schema::{ source::{ actor_language::CommunityLanguage, community::Community, - post::{Post, PostForm, PostLike, PostLikeForm}, + local_site::LocalSite, + post::{Post, PostInsertForm, PostLike, PostLikeForm, PostUpdateForm}, }, traits::{Crud, Likeable}, - utils::diesel_option_overwrite, }; use lemmy_db_views_actor::structs::CommunityView; use lemmy_utils::{ @@ -52,15 +53,15 @@ impl PerformCrud for CreatePost { let data: &CreatePost = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - let slur_regex = &context.settings().slur_regex(); - check_slurs(&data.name, slur_regex)?; - check_slurs_opt(&data.body, slur_regex)?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&data.name, &slur_regex)?; + check_slurs_opt(&data.body, &slur_regex)?; honeypot_check(&data.honeypot)?; let data_url = data.url.as_ref(); - let url = Some(data_url.map(clean_url_params).map(Into::into)); // TODO no good way to handle a "clear" - let body = diesel_option_overwrite(&data.body); + let url = data_url.map(clean_url_params).map(Into::into); // TODO no good way to handle a "clear" if !is_valid_post_title(&data.name) { return Err(LemmyError::from_message("invalid_post_title")); @@ -89,7 +90,7 @@ impl PerformCrud for CreatePost { let (metadata_res, thumbnail_url) = fetch_site_data(context.client(), context.settings(), data_url).await; let (embed_title, embed_description, embed_video_url) = metadata_res - .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url))) + .map(|u| (u.title, u.description, u.embed_video_url)) .unwrap_or_default(); let language_id = match data.language_id { @@ -106,20 +107,19 @@ impl PerformCrud for CreatePost { }) .await??; - let post_form = PostForm { - name: data.name.trim().to_owned(), - url, - body, - community_id: data.community_id, - creator_id: local_user_view.person.id, - nsfw: data.nsfw, - embed_title, - embed_description, - embed_video_url, - language_id, - thumbnail_url: Some(thumbnail_url), - ..PostForm::default() - }; + let post_form = PostInsertForm::builder() + .name(data.name.trim().to_owned()) + .url(url) + .body(data.body.to_owned()) + .community_id(data.community_id) + .creator_id(local_user_view.person.id) + .nsfw(data.nsfw) + .embed_title(embed_title) + .embed_description(embed_description) + .embed_video_url(embed_video_url) + .language_id(language_id) + .thumbnail_url(thumbnail_url) + .build(); let inserted_post = match blocking(context.pool(), move |conn| Post::create(conn, &post_form)).await? { @@ -143,7 +143,11 @@ impl PerformCrud for CreatePost { &inserted_post_id.to_string(), &protocol_and_hostname, )?; - Ok(Post::update_ap_id(conn, inserted_post_id, apub_id)?) + Ok(Post::update( + conn, + inserted_post_id, + &PostUpdateForm::builder().ap_id(Some(apub_id)).build(), + )?) }) .await? .map_err(|e| e.with_message("couldnt_create_post"))?; diff --git a/crates/api_crud/src/post/delete.rs b/crates/api_crud/src/post/delete.rs index 734a0246..78e2f49a 100644 --- a/crates/api_crud/src/post/delete.rs +++ b/crates/api_crud/src/post/delete.rs @@ -11,7 +11,10 @@ use lemmy_api_common::{ }; use lemmy_apub::activities::deletion::{send_apub_delete_in_community, DeletableObjects}; use lemmy_db_schema::{ - source::{community::Community, post::Post}, + source::{ + community::Community, + post::{Post, PostUpdateForm}, + }, traits::Crud, }; use lemmy_utils::{error::LemmyError, ConnectionId}; @@ -56,7 +59,11 @@ impl PerformCrud for DeletePost { let post_id = data.post_id; let deleted = data.deleted; let updated_post = blocking(context.pool(), move |conn| { - Post::update_deleted(conn, post_id, deleted) + Post::update( + conn, + post_id, + &PostUpdateForm::builder().deleted(Some(deleted)).build(), + ) }) .await??; diff --git a/crates/api_crud/src/post/list.rs b/crates/api_crud/src/post/list.rs index 647d6b29..f7f7b2cc 100644 --- a/crates/api_crud/src/post/list.rs +++ b/crates/api_crud/src/post/list.rs @@ -10,7 +10,10 @@ use lemmy_api_common::{ }, }; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity}; -use lemmy_db_schema::{source::community::Community, traits::DeleteableOrRemoveable}; +use lemmy_db_schema::{ + source::{community::Community, local_site::LocalSite}, + traits::DeleteableOrRemoveable, +}; use lemmy_db_views::post_view::PostQuery; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::LemmyContext; @@ -29,13 +32,14 @@ impl PerformCrud for GetPosts { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let is_logged_in = local_user_view.is_some(); let sort = data.sort; - let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?; + let listing_type = listing_type_with_site_default(data.type_, &local_site)?; let page = data.page; let limit = data.limit; diff --git a/crates/api_crud/src/post/read.rs b/crates/api_crud/src/post/read.rs index a59e8197..746b8470 100644 --- a/crates/api_crud/src/post/read.rs +++ b/crates/api_crud/src/post/read.rs @@ -6,7 +6,7 @@ use lemmy_api_common::{ }; use lemmy_db_schema::{ aggregates::structs::{PersonPostAggregates, PersonPostAggregatesForm}, - source::comment::Comment, + source::{comment::Comment, local_site::LocalSite}, traits::{Crud, DeleteableOrRemoveable}, }; use lemmy_db_views::structs::PostView; @@ -28,8 +28,9 @@ impl PerformCrud for GetPost { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - check_private_instance(&local_user_view, context.pool()).await?; + check_private_instance(&local_user_view, &local_site)?; let person_id = local_user_view.map(|u| u.person.id); diff --git a/crates/api_crud/src/post/remove.rs b/crates/api_crud/src/post/remove.rs index 9ba29158..d646a008 100644 --- a/crates/api_crud/src/post/remove.rs +++ b/crates/api_crud/src/post/remove.rs @@ -9,7 +9,7 @@ use lemmy_db_schema::{ source::{ community::Community, moderator::{ModRemovePost, ModRemovePostForm}, - post::Post, + post::{Post, PostUpdateForm}, }, traits::Crud, }; @@ -52,7 +52,11 @@ impl PerformCrud for RemovePost { let post_id = data.post_id; let removed = data.removed; let updated_post = blocking(context.pool(), move |conn| { - Post::update_removed(conn, post_id, removed) + Post::update( + conn, + post_id, + &PostUpdateForm::builder().removed(Some(removed)).build(), + ) }) .await??; diff --git a/crates/api_crud/src/post/update.rs b/crates/api_crud/src/post/update.rs index 24cb5f08..ab9993e6 100644 --- a/crates/api_crud/src/post/update.rs +++ b/crates/api_crud/src/post/update.rs @@ -8,6 +8,7 @@ use lemmy_api_common::{ check_community_ban, check_community_deleted_or_removed, get_local_user_view_from_jwt, + local_site_to_slur_regex, }, }; use lemmy_apub::protocol::activities::{ @@ -17,10 +18,11 @@ use lemmy_apub::protocol::activities::{ use lemmy_db_schema::{ source::{ actor_language::CommunityLanguage, - post::{Post, PostForm}, + local_site::LocalSite, + post::{Post, PostUpdateForm}, }, traits::Crud, - utils::{diesel_option_overwrite, naive_now}, + utils::diesel_option_overwrite, }; use lemmy_utils::{ error::LemmyError, @@ -42,6 +44,7 @@ impl PerformCrud for EditPost { let data: &EditPost = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; let data_url = data.url.as_ref(); @@ -50,9 +53,9 @@ impl PerformCrud for EditPost { let url = Some(data_url.map(clean_url_params).map(Into::into)); let body = diesel_option_overwrite(&data.body); - let slur_regex = &context.settings().slur_regex(); - check_slurs_opt(&data.name, slur_regex)?; - check_slurs_opt(&data.body, slur_regex)?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs_opt(&data.name, &slur_regex)?; + check_slurs_opt(&data.body, &slur_regex)?; if let Some(name) = &data.name { if !is_valid_post_title(name) { @@ -90,21 +93,17 @@ impl PerformCrud for EditPost { }) .await??; - let post_form = PostForm { - creator_id: orig_post.creator_id.to_owned(), - community_id: orig_post.community_id, - name: data.name.to_owned().unwrap_or(orig_post.name), - url, - body, - nsfw: data.nsfw, - updated: Some(naive_now()), - embed_title, - embed_description, - embed_video_url, - language_id: data.language_id, - thumbnail_url: Some(thumbnail_url), - ..PostForm::default() - }; + let post_form = PostUpdateForm::builder() + .name(data.name.to_owned()) + .url(url) + .body(body) + .nsfw(data.nsfw) + .embed_title(embed_title) + .embed_description(embed_description) + .embed_video_url(embed_video_url) + .language_id(data.language_id) + .thumbnail_url(Some(thumbnail_url)) + .build(); let post_id = data.post_id; let res = blocking(context.pool(), move |conn| { diff --git a/crates/api_crud/src/private_message/create.rs b/crates/api_crud/src/private_message/create.rs index 27803131..9eeea66d 100644 --- a/crates/api_crud/src/private_message/create.rs +++ b/crates/api_crud/src/private_message/create.rs @@ -7,6 +7,7 @@ use lemmy_api_common::{ check_person_block, get_interface_language, get_local_user_view_from_jwt, + local_site_to_slur_regex, send_email_to_user, }, }; @@ -19,7 +20,10 @@ use lemmy_apub::{ EndpointType, }; use lemmy_db_schema::{ - source::private_message::{PrivateMessage, PrivateMessageForm}, + source::{ + local_site::LocalSite, + private_message::{PrivateMessage, PrivateMessageInsertForm, PrivateMessageUpdateForm}, + }, traits::Crud, }; use lemmy_db_views::structs::LocalUserView; @@ -39,18 +43,20 @@ impl PerformCrud for CreatePrivateMessage { let data: &CreatePrivateMessage = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; - let content_slurs_removed = - remove_slurs(&data.content.to_owned(), &context.settings().slur_regex()); + let content_slurs_removed = remove_slurs( + &data.content.to_owned(), + &local_site_to_slur_regex(&local_site), + ); check_person_block(local_user_view.person.id, data.recipient_id, context.pool()).await?; - let private_message_form = PrivateMessageForm { - content: content_slurs_removed.to_owned(), - creator_id: local_user_view.person.id, - recipient_id: data.recipient_id, - ..PrivateMessageForm::default() - }; + let private_message_form = PrivateMessageInsertForm::builder() + .content(content_slurs_removed.to_owned()) + .creator_id(local_user_view.person.id) + .recipient_id(data.recipient_id) + .build(); let inserted_private_message = match blocking(context.pool(), move |conn| { PrivateMessage::create(conn, &private_message_form) @@ -76,10 +82,12 @@ impl PerformCrud for CreatePrivateMessage { &inserted_private_message_id.to_string(), &protocol_and_hostname, )?; - Ok(PrivateMessage::update_ap_id( + Ok(PrivateMessage::update( conn, - inserted_private_message_id, - apub_id, + inserted_private_message.id, + &PrivateMessageUpdateForm::builder() + .ap_id(Some(apub_id)) + .build(), )?) }, ) diff --git a/crates/api_crud/src/private_message/delete.rs b/crates/api_crud/src/private_message/delete.rs index e7d67020..8a348223 100644 --- a/crates/api_crud/src/private_message/delete.rs +++ b/crates/api_crud/src/private_message/delete.rs @@ -5,7 +5,10 @@ use lemmy_api_common::{ utils::{blocking, get_local_user_view_from_jwt}, }; use lemmy_apub::activities::deletion::send_apub_delete_private_message; -use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud}; +use lemmy_db_schema::{ + source::private_message::{PrivateMessage, PrivateMessageUpdateForm}, + traits::Crud, +}; use lemmy_utils::{error::LemmyError, ConnectionId}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; @@ -37,7 +40,13 @@ impl PerformCrud for DeletePrivateMessage { let private_message_id = data.private_message_id; let deleted = data.deleted; let updated_private_message = blocking(context.pool(), move |conn| { - PrivateMessage::update_deleted(conn, private_message_id, deleted) + PrivateMessage::update( + conn, + private_message_id, + &PrivateMessageUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; diff --git a/crates/api_crud/src/private_message/update.rs b/crates/api_crud/src/private_message/update.rs index 9de33a69..9782b64c 100644 --- a/crates/api_crud/src/private_message/update.rs +++ b/crates/api_crud/src/private_message/update.rs @@ -2,13 +2,20 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ private_message::{EditPrivateMessage, PrivateMessageResponse}, - utils::{blocking, get_local_user_view_from_jwt}, + utils::{blocking, get_local_user_view_from_jwt, local_site_to_slur_regex}, }; use lemmy_apub::protocol::activities::{ create_or_update::private_message::CreateOrUpdatePrivateMessage, CreateOrUpdateType, }; -use lemmy_db_schema::{source::private_message::PrivateMessage, traits::Crud}; +use lemmy_db_schema::{ + source::{ + local_site::LocalSite, + private_message::{PrivateMessage, PrivateMessageUpdateForm}, + }, + traits::Crud, + utils::naive_now, +}; use lemmy_utils::{error::LemmyError, utils::remove_slurs, ConnectionId}; use lemmy_websocket::{send::send_pm_ws_message, LemmyContext, UserOperationCrud}; @@ -25,6 +32,7 @@ impl PerformCrud for EditPrivateMessage { let data: &EditPrivateMessage = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; // Checking permissions let private_message_id = data.private_message_id; @@ -37,10 +45,17 @@ impl PerformCrud for EditPrivateMessage { } // Doing the update - let content_slurs_removed = remove_slurs(&data.content, &context.settings().slur_regex()); + let content_slurs_removed = remove_slurs(&data.content, &local_site_to_slur_regex(&local_site)); let private_message_id = data.private_message_id; let updated_private_message = blocking(context.pool(), move |conn| { - PrivateMessage::update_content(conn, private_message_id, &content_slurs_removed) + PrivateMessage::update( + conn, + private_message_id, + &PrivateMessageUpdateForm::builder() + .content(Some(content_slurs_removed)) + .updated(Some(Some(naive_now()))) + .build(), + ) }) .await? .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_private_message"))?; diff --git a/crates/api_crud/src/site/create.rs b/crates/api_crud/src/site/create.rs index 8bcdda46..5340c657 100644 --- a/crates/api_crud/src/site/create.rs +++ b/crates/api_crud/src/site/create.rs @@ -3,19 +3,29 @@ use activitypub_federation::core::signatures::generate_actor_keypair; use actix_web::web::Data; use lemmy_api_common::{ site::{CreateSite, SiteResponse}, - utils::{blocking, get_local_user_view_from_jwt, is_admin, site_description_length_check}, + utils::{ + blocking, + get_local_user_view_from_jwt, + is_admin, + local_site_to_slur_regex, + site_description_length_check, + }, }; use lemmy_apub::generate_site_inbox_url; use lemmy_db_schema::{ newtypes::DbUrl, - source::site::{Site, SiteForm}, + source::{ + local_site::{LocalSite, LocalSiteUpdateForm}, + local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm}, + site::{Site, SiteUpdateForm}, + }, traits::Crud, utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, }; use lemmy_db_views::structs::SiteView; use lemmy_utils::{ error::LemmyError, - utils::{check_slurs, check_slurs_opt}, + utils::{check_application_question, check_slurs, check_slurs_opt}, ConnectionId, }; use lemmy_websocket::LemmyContext; @@ -33,8 +43,9 @@ impl PerformCrud for CreateSite { ) -> Result { let data: &CreateSite = self; - let read_site = Site::read_local; - if blocking(context.pool(), read_site).await?.is_ok() { + let local_site = blocking(context.pool(), LocalSite::read).await??; + + if local_site.site_setup { return Err(LemmyError::from_message("site_already_exists")); }; @@ -46,8 +57,9 @@ impl PerformCrud for CreateSite { let icon = diesel_option_overwrite_to_url(&data.icon)?; let banner = diesel_option_overwrite_to_url(&data.banner)?; - check_slurs(&data.name, &context.settings().slur_regex())?; - check_slurs_opt(&data.description, &context.settings().slur_regex())?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&data.name, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; // Make sure user is an admin is_admin(&local_user_view)?; @@ -56,35 +68,82 @@ impl PerformCrud for CreateSite { site_description_length_check(desc)?; } + let application_question = diesel_option_overwrite(&data.application_question); + check_application_question(&application_question, &data.require_application)?; + let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into(); let inbox_url = Some(generate_site_inbox_url(&actor_id)?); let keypair = generate_actor_keypair()?; - let site_form = SiteForm { - name: data.name.to_owned(), - sidebar, - description, - icon, - banner, - enable_downvotes: data.enable_downvotes, - open_registration: data.open_registration, - enable_nsfw: data.enable_nsfw, - community_creation_admin_only: data.community_creation_admin_only, - actor_id: Some(actor_id), - last_refreshed_at: Some(naive_now()), - inbox_url, - private_key: Some(Some(keypair.private_key)), - public_key: Some(keypair.public_key), - default_theme: data.default_theme.clone(), - default_post_listing_type: data.default_post_listing_type.clone(), - application_email_admins: data.application_email_admins, - hide_modlog_mod_names: data.hide_modlog_mod_names, - ..SiteForm::default() - }; + let site_form = SiteUpdateForm::builder() + .name(Some(data.name.to_owned())) + .sidebar(sidebar) + .description(description) + .icon(icon) + .banner(banner) + .actor_id(Some(actor_id)) + .last_refreshed_at(Some(naive_now())) + .inbox_url(inbox_url) + .private_key(Some(Some(keypair.private_key))) + .public_key(Some(keypair.public_key)) + .build(); + + let site_id = local_site.site_id; + blocking(context.pool(), move |conn| { + Site::update(conn, site_id, &site_form) + }) + .await??; + + let local_site_form = LocalSiteUpdateForm::builder() + // Set the site setup to true + .site_setup(Some(true)) + .enable_downvotes(data.enable_downvotes) + .open_registration(data.open_registration) + .enable_nsfw(data.enable_nsfw) + .community_creation_admin_only(data.community_creation_admin_only) + .require_email_verification(data.require_email_verification) + .require_application(data.require_application) + .application_question(application_question) + .private_instance(data.private_instance) + .default_theme(data.default_theme.clone()) + .default_post_listing_type(data.default_post_listing_type.clone()) + .legal_information(diesel_option_overwrite(&data.legal_information)) + .application_email_admins(data.application_email_admins) + .hide_modlog_mod_names(data.hide_modlog_mod_names) + .updated(Some(Some(naive_now()))) + .slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex)) + .actor_name_max_length(data.actor_name_max_length) + .federation_enabled(data.federation_enabled) + .federation_debug(data.federation_debug) + .federation_strict_allowlist(data.federation_strict_allowlist) + .federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit) + .federation_worker_count(data.federation_worker_count) + .captcha_enabled(data.captcha_enabled) + .captcha_difficulty(data.captcha_difficulty.to_owned()) + .build(); + blocking(context.pool(), move |conn| { + LocalSite::update(conn, &local_site_form) + }) + .await??; + + let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder() + .message(data.rate_limit_message) + .message_per_second(data.rate_limit_message_per_second) + .post(data.rate_limit_post) + .post_per_second(data.rate_limit_post_per_second) + .register(data.rate_limit_register) + .register_per_second(data.rate_limit_register_per_second) + .image(data.rate_limit_image) + .image_per_second(data.rate_limit_image_per_second) + .comment(data.rate_limit_comment) + .comment_per_second(data.rate_limit_comment_per_second) + .search(data.rate_limit_search) + .search_per_second(data.rate_limit_search_per_second) + .build(); - let create_site = move |conn: &mut _| Site::create(conn, &site_form); - blocking(context.pool(), create_site) - .await? - .map_err(|e| LemmyError::from_error_message(e, "site_already_exists"))?; + blocking(context.pool(), move |conn| { + LocalSiteRateLimit::update(conn, &local_site_rate_limit_form) + }) + .await??; let site_view = blocking(context.pool(), SiteView::read_local).await??; diff --git a/crates/api_crud/src/site/read.rs b/crates/api_crud/src/site/read.rs index fc3293a7..226e24f3 100644 --- a/crates/api_crud/src/site/read.rs +++ b/crates/api_crud/src/site/read.rs @@ -1,8 +1,7 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ - person::Register, - site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo}, + site::{GetSite, GetSiteResponse, MyUserInfo}, utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt}, }; use lemmy_db_schema::source::{actor_language::SiteLanguage, language::Language}; @@ -16,56 +15,20 @@ use lemmy_db_views_actor::structs::{ }; use lemmy_utils::{error::LemmyError, version, ConnectionId}; use lemmy_websocket::{messages::GetUsersOnline, LemmyContext}; -use tracing::info; #[async_trait::async_trait(?Send)] impl PerformCrud for GetSite { type Response = GetSiteResponse; - #[tracing::instrument(skip(context, websocket_id))] + #[tracing::instrument(skip(context, _websocket_id))] async fn perform( &self, context: &Data, - websocket_id: Option, + _websocket_id: Option, ) -> Result { let data: &GetSite = self; - let site_view = match blocking(context.pool(), SiteView::read_local).await? { - Ok(site_view) => Some(site_view), - // If the site isn't created yet, check the setup - Err(_) => { - if let Some(setup) = context.settings().setup.as_ref() { - let register = Register { - username: setup.admin_username.to_owned(), - email: setup.admin_email.clone().map(|s| s.into()), - password: setup.admin_password.clone().into(), - password_verify: setup.admin_password.clone().into(), - show_nsfw: true, - captcha_uuid: None, - captcha_answer: None, - honeypot: None, - answer: None, - }; - let admin_jwt = register - .perform(context, websocket_id) - .await? - .jwt - .expect("jwt is returned from registration on newly created site"); - info!("Admin {} created", setup.admin_username); - - let create_site = CreateSite { - name: setup.site_name.to_owned(), - auth: admin_jwt, - ..CreateSite::default() - }; - create_site.perform(context, websocket_id).await?; - info!("Site {} created", setup.site_name); - Some(blocking(context.pool(), SiteView::read_local).await??) - } else { - None - } - } - }; + let site_view = blocking(context.pool(), SiteView::read_local).await??; let admins = blocking(context.pool(), PersonViewSafe::admins).await??; @@ -130,7 +93,8 @@ impl PerformCrud for GetSite { None }; - let federated_instances = build_federated_instances(context.pool(), context.settings()).await?; + let federated_instances = + build_federated_instances(&site_view.local_site, context.pool()).await?; let all_languages = blocking(context.pool(), Language::read_all).await??; let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??; diff --git a/crates/api_crud/src/site/update.rs b/crates/api_crud/src/site/update.rs index 788546ea..47915e43 100644 --- a/crates/api_crud/src/site/update.rs +++ b/crates/api_crud/src/site/update.rs @@ -2,22 +2,36 @@ use crate::PerformCrud; use actix_web::web::Data; use lemmy_api_common::{ site::{EditSite, SiteResponse}, - utils::{blocking, get_local_user_view_from_jwt, is_admin, site_description_length_check}, + utils::{ + blocking, + get_local_user_view_from_jwt, + is_admin, + local_site_to_slur_regex, + site_description_length_check, + }, }; use lemmy_db_schema::{ source::{ actor_language::SiteLanguage, + federation_allowlist::FederationAllowList, + federation_blocklist::FederationBlockList, + local_site::{LocalSite, LocalSiteUpdateForm}, + local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm}, local_user::LocalUser, - site::{Site, SiteForm}, + site::{Site, SiteUpdateForm}, }, traits::Crud, utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now}, ListingType, }; use lemmy_db_views::structs::SiteView; -use lemmy_utils::{error::LemmyError, utils::check_slurs_opt, ConnectionId}; +use lemmy_utils::{ + error::LemmyError, + utils::{check_application_question, check_slurs_opt}, + ConnectionId, +}; use lemmy_websocket::{messages::SendAllMessage, LemmyContext, UserOperationCrud}; -use std::{default::Default, str::FromStr}; +use std::str::FromStr; #[async_trait::async_trait(?Send)] impl PerformCrud for EditSite { @@ -32,32 +46,22 @@ impl PerformCrud for EditSite { let data: &EditSite = self; let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; // Make sure user is an admin is_admin(&local_user_view)?; - let local_site = blocking(context.pool(), Site::read_local).await??; - - let sidebar = diesel_option_overwrite(&data.sidebar); - let description = diesel_option_overwrite(&data.description); - let application_question = diesel_option_overwrite(&data.application_question); - let legal_information = diesel_option_overwrite(&data.legal_information); - let icon = diesel_option_overwrite_to_url(&data.icon)?; - let banner = diesel_option_overwrite_to_url(&data.banner)?; + let slur_regex = local_site_to_slur_regex(&local_site); - check_slurs_opt(&data.name, &context.settings().slur_regex())?; - check_slurs_opt(&data.description, &context.settings().slur_regex())?; + check_slurs_opt(&data.name, &slur_regex)?; + check_slurs_opt(&data.description, &slur_regex)?; - if let Some(Some(desc)) = &description { + if let Some(desc) = &data.description { site_description_length_check(desc)?; } - // Make sure if applications are required, that there is an application questionnaire - if data.require_application.unwrap_or(false) - && application_question.as_ref().unwrap_or(&None).is_none() - { - return Err(LemmyError::from_message("application_question_required")); - } + let application_question = diesel_option_overwrite(&data.application_question); + check_application_question(&application_question, &data.require_application)?; if let Some(default_post_listing_type) = &data.default_post_listing_type { // only allow all or local as default listing types @@ -69,7 +73,7 @@ impl PerformCrud for EditSite { } } - let site_id = local_site.id; + let site_id = local_site.site_id; if let Some(discussion_languages) = data.discussion_languages.clone() { blocking(context.pool(), move |conn| { SiteLanguage::update(conn, discussion_languages.clone(), site_id) @@ -77,41 +81,104 @@ impl PerformCrud for EditSite { .await??; } - let site_form = SiteForm { - name: data.name.to_owned().unwrap_or(local_site.name), - sidebar, - description, - icon, - banner, - updated: Some(naive_now()), - enable_downvotes: data.enable_downvotes, - open_registration: data.open_registration, - enable_nsfw: data.enable_nsfw, - community_creation_admin_only: data.community_creation_admin_only, - require_email_verification: data.require_email_verification, - require_application: data.require_application, - application_question, - private_instance: data.private_instance, - default_theme: data.default_theme.clone(), - default_post_listing_type: data.default_post_listing_type.clone(), - legal_information, - application_email_admins: data.application_email_admins, - hide_modlog_mod_names: data.hide_modlog_mod_names, - ..SiteForm::default() - }; - - let update_site = blocking(context.pool(), move |conn| { - Site::update(conn, local_site.id, &site_form) + let name = data.name.to_owned(); + let site_form = SiteUpdateForm::builder() + .name(name) + .sidebar(diesel_option_overwrite(&data.sidebar)) + .description(diesel_option_overwrite(&data.description)) + .icon(diesel_option_overwrite_to_url(&data.icon)?) + .banner(diesel_option_overwrite_to_url(&data.banner)?) + .updated(Some(Some(naive_now()))) + .build(); + + blocking(context.pool(), move |conn| { + Site::update(conn, site_id, &site_form) + }) + .await + // Ignore errors for all these, so as to not throw errors if no update occurs + // Diesel will throw an error for empty update forms + .ok(); + + let local_site_form = LocalSiteUpdateForm::builder() + .enable_downvotes(data.enable_downvotes) + .open_registration(data.open_registration) + .enable_nsfw(data.enable_nsfw) + .community_creation_admin_only(data.community_creation_admin_only) + .require_email_verification(data.require_email_verification) + .require_application(data.require_application) + .application_question(application_question) + .private_instance(data.private_instance) + .default_theme(data.default_theme.clone()) + .default_post_listing_type(data.default_post_listing_type.clone()) + .legal_information(diesel_option_overwrite(&data.legal_information)) + .application_email_admins(data.application_email_admins) + .hide_modlog_mod_names(data.hide_modlog_mod_names) + .updated(Some(Some(naive_now()))) + .slur_filter_regex(diesel_option_overwrite(&data.slur_filter_regex)) + .actor_name_max_length(data.actor_name_max_length) + .federation_enabled(data.federation_enabled) + .federation_debug(data.federation_debug) + .federation_strict_allowlist(data.federation_strict_allowlist) + .federation_http_fetch_retry_limit(data.federation_http_fetch_retry_limit) + .federation_worker_count(data.federation_worker_count) + .captcha_enabled(data.captcha_enabled) + .captcha_difficulty(data.captcha_difficulty.to_owned()) + .build(); + + let update_local_site = blocking(context.pool(), move |conn| { + LocalSite::update(conn, &local_site_form) }) - .await? - .map_err(|e| LemmyError::from_error_message(e, "couldnt_update_site"))?; + .await + .ok(); + + let local_site_rate_limit_form = LocalSiteRateLimitUpdateForm::builder() + .message(data.rate_limit_message) + .message_per_second(data.rate_limit_message_per_second) + .post(data.rate_limit_post) + .post_per_second(data.rate_limit_post_per_second) + .register(data.rate_limit_register) + .register_per_second(data.rate_limit_register_per_second) + .image(data.rate_limit_image) + .image_per_second(data.rate_limit_image_per_second) + .comment(data.rate_limit_comment) + .comment_per_second(data.rate_limit_comment_per_second) + .search(data.rate_limit_search) + .search_per_second(data.rate_limit_search_per_second) + .build(); + + blocking(context.pool(), move |conn| { + LocalSiteRateLimit::update(conn, &local_site_rate_limit_form) + }) + .await + .ok(); + + // Replace the blocked and allowed instances + let allowed = data.allowed_instances.to_owned(); + blocking(context.pool(), move |conn| { + FederationAllowList::replace(conn, allowed) + }) + .await??; + let blocked = data.blocked_instances.to_owned(); + blocking(context.pool(), move |conn| { + FederationBlockList::replace(conn, blocked) + }) + .await??; // TODO can't think of a better way to do this. // If the server suddenly requires email verification, or required applications, no old users // will be able to log in. It really only wants this to be a requirement for NEW signups. // So if it was set from false, to true, you need to update all current users columns to be verified. - if !local_site.require_application && update_site.require_application { + let new_require_application = update_local_site + .as_ref() + .map(|ols| { + ols + .as_ref() + .map(|ls| ls.require_application) + .unwrap_or(false) + }) + .unwrap_or(false); + if !local_site.require_application && new_require_application { blocking(context.pool(), move |conn| { LocalUser::set_all_users_registration_applications_accepted(conn) }) @@ -119,7 +186,16 @@ impl PerformCrud for EditSite { .map_err(|e| LemmyError::from_error_message(e, "couldnt_set_all_registrations_accepted"))?; } - if !local_site.require_email_verification && update_site.require_email_verification { + let new_require_email_verification = update_local_site + .as_ref() + .map(|ols| { + ols + .as_ref() + .map(|ls| ls.require_email_verification) + .unwrap_or(false) + }) + .unwrap_or(false); + if !local_site.require_email_verification && new_require_email_verification { blocking(context.pool(), move |conn| { LocalUser::set_all_users_email_verified(conn) }) diff --git a/crates/api_crud/src/user/create.rs b/crates/api_crud/src/user/create.rs index 6560783b..88bc43d0 100644 --- a/crates/api_crud/src/user/create.rs +++ b/crates/api_crud/src/user/create.rs @@ -6,6 +6,7 @@ use lemmy_api_common::{ utils::{ blocking, honeypot_check, + local_site_to_slur_regex, password_length_check, send_new_applicant_email_to_admins, send_verification_email, @@ -20,14 +21,13 @@ use lemmy_apub::{ use lemmy_db_schema::{ aggregates::structs::PersonAggregates, source::{ - local_user::{LocalUser, LocalUserForm}, - person::{Person, PersonForm}, - registration_application::{RegistrationApplication, RegistrationApplicationForm}, - site::Site, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm}, + registration_application::{RegistrationApplication, RegistrationApplicationInsertForm}, }, traits::Crud, }; -use lemmy_db_views::structs::LocalUserView; +use lemmy_db_views::structs::{LocalUserView, SiteView}; use lemmy_db_views_actor::structs::PersonViewSafe; use lemmy_utils::{ claims::Claims, @@ -49,27 +49,21 @@ impl PerformCrud for Register { ) -> Result { let data: &Register = self; - // no email verification, or applications if the site is not setup yet - let (mut email_verification, mut require_application) = (false, false); + let site_view = blocking(context.pool(), SiteView::read_local).await??; + let local_site = site_view.local_site; - // Make sure site has open registration - let site = blocking(context.pool(), Site::read_local).await?; - if let Ok(site) = &site { - if !site.open_registration { - return Err(LemmyError::from_message("registration_closed")); - } - email_verification = site.require_email_verification; - require_application = site.require_application; + if !local_site.open_registration { + return Err(LemmyError::from_message("registration_closed")); } password_length_check(&data.password)?; honeypot_check(&data.honeypot)?; - if email_verification && data.email.is_none() { + if local_site.require_email_verification && data.email.is_none() { return Err(LemmyError::from_message("email_required")); } - if require_application && data.answer.is_none() { + if local_site.require_application && data.answer.is_none() { return Err(LemmyError::from_message( "registration_application_answer_required", )); @@ -87,7 +81,7 @@ impl PerformCrud for Register { .await??; // If its not the admin, check the captcha - if !no_admins && context.settings().captcha.enabled { + if !no_admins && local_site.captcha_enabled { let check = context .chat_server() .send(CheckCaptcha { @@ -106,12 +100,12 @@ impl PerformCrud for Register { } } - let slur_regex = &context.settings().slur_regex(); - check_slurs(&data.username, slur_regex)?; - check_slurs_opt(&data.answer, slur_regex)?; + let slur_regex = local_site_to_slur_regex(&local_site); + check_slurs(&data.username, &slur_regex)?; + check_slurs_opt(&data.answer, &slur_regex)?; let actor_keypair = generate_actor_keypair()?; - if !is_valid_actor_name(&data.username, context.settings().actor_name_max_length) { + if !is_valid_actor_name(&data.username, local_site.actor_name_max_length as usize) { return Err(LemmyError::from_message("invalid_username")); } let actor_id = generate_local_apub_endpoint( @@ -123,16 +117,16 @@ impl PerformCrud for Register { // We have to create both a person, and local_user // Register the new person - let person_form = PersonForm { - name: data.username.to_owned(), - actor_id: Some(actor_id.clone()), - private_key: Some(Some(actor_keypair.private_key)), - public_key: Some(actor_keypair.public_key), - inbox_url: Some(generate_inbox_url(&actor_id)?), - shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)), - admin: Some(no_admins), - ..PersonForm::default() - }; + let person_form = PersonInsertForm::builder() + .name(data.username.to_owned()) + .actor_id(Some(actor_id.clone())) + .private_key(Some(actor_keypair.private_key)) + .public_key(actor_keypair.public_key) + .inbox_url(Some(generate_inbox_url(&actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&actor_id)?)) + .admin(Some(no_admins)) + .instance_id(site_view.site.instance_id) + .build(); // insert the person let inserted_person = blocking(context.pool(), move |conn| { @@ -142,17 +136,15 @@ impl PerformCrud for Register { .map_err(|e| LemmyError::from_error_message(e, "user_already_exists"))?; // Create the local user - let local_user_form = LocalUserForm { - person_id: Some(inserted_person.id), - email: Some(data.email.as_deref().map(|s| s.to_lowercase())), - password_encrypted: Some(data.password.to_string()), - show_nsfw: Some(data.show_nsfw), - email_verified: Some(false), - ..LocalUserForm::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .email(data.email.as_deref().map(|s| s.to_lowercase())) + .password_encrypted(data.password.to_string()) + .show_nsfw(Some(data.show_nsfw)) + .build(); let inserted_local_user = match blocking(context.pool(), move |conn| { - LocalUser::register(conn, &local_user_form) + LocalUser::create(conn, &local_user_form) }) .await? { @@ -176,13 +168,12 @@ impl PerformCrud for Register { } }; - if require_application { + if local_site.require_application { // Create the registration application - let form = RegistrationApplicationForm { - local_user_id: Some(inserted_local_user.id), + let form = RegistrationApplicationInsertForm { + local_user_id: inserted_local_user.id, // We already made sure answer was not null above - answer: data.answer.to_owned(), - ..RegistrationApplicationForm::default() + answer: data.answer.to_owned().expect("must have an answer"), }; blocking(context.pool(), move |conn| { @@ -192,7 +183,7 @@ impl PerformCrud for Register { } // Email the admins - if site.map(|s| s.application_email_admins).unwrap_or(false) { + if local_site.application_email_admins { send_new_applicant_email_to_admins(&data.username, context.pool(), context.settings()) .await?; } @@ -204,7 +195,7 @@ impl PerformCrud for Register { }; // Log the user in directly if email verification and application aren't required - if !require_application && !email_verification { + if !local_site.require_application && !local_site.require_email_verification { login_response.jwt = Some( Claims::jwt( inserted_local_user.id.0, @@ -214,7 +205,7 @@ impl PerformCrud for Register { .into(), ); } else { - if email_verification { + if local_site.require_email_verification { let local_user_view = LocalUserView { local_user: inserted_local_user, person: inserted_person, @@ -226,12 +217,13 @@ impl PerformCrud for Register { .email .clone() .expect("email was provided"); + send_verification_email(&local_user_view, &email, context.pool(), context.settings()) .await?; login_response.verify_email_sent = true; } - if require_application { + if local_site.require_application { login_response.registration_created = true; } } diff --git a/crates/api_crud/src/user/read.rs b/crates/api_crud/src/user/read.rs index e77a4b3f..5e6355ce 100644 --- a/crates/api_crud/src/user/read.rs +++ b/crates/api_crud/src/user/read.rs @@ -5,7 +5,10 @@ use lemmy_api_common::{ utils::{blocking, check_private_instance, get_local_user_view_from_jwt_opt}, }; use lemmy_apub::{fetcher::resolve_actor_identifier, objects::person::ApubPerson}; -use lemmy_db_schema::{source::person::Person, utils::post_to_comment_sort_type}; +use lemmy_db_schema::{ + source::{local_site::LocalSite, person::Person}, + utils::post_to_comment_sort_type, +}; use lemmy_db_views::{comment_view::CommentQuery, post_view::PostQuery}; use lemmy_db_views_actor::structs::{CommunityModeratorView, PersonViewSafe}; use lemmy_utils::{error::LemmyError, ConnectionId}; @@ -31,7 +34,9 @@ impl PerformCrud for GetPersonDetails { let local_user_view = get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret()) .await?; - check_private_instance(&local_user_view, context.pool()).await?; + let local_site = blocking(context.pool(), LocalSite::read).await??; + + check_private_instance(&local_user_view, &local_site)?; let person_details_id = match data.person_id { Some(id) => id, diff --git a/crates/apub/src/activities/block/block_user.rs b/crates/apub/src/activities/block/block_user.rs index 72c43163..3e574232 100644 --- a/crates/apub/src/activities/block/block_user.rs +++ b/crates/apub/src/activities/block/block_user.rs @@ -9,6 +9,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::block::block_user::BlockUser, @@ -33,7 +35,7 @@ use lemmy_db_schema::{ CommunityPersonBanForm, }, moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, - person::Person, + person::{Person, PersonUpdateForm}, }, traits::{Bannable, Crud, Followable}, }; @@ -123,6 +125,10 @@ impl ActivityHandler for BlockUser { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; match self .target @@ -177,7 +183,14 @@ impl ActivityHandler for BlockUser { match target { SiteOrCommunity::Site(_site) => { let blocked_person = blocking(context.pool(), move |conn| { - Person::ban_person(conn, blocked_person.id, true, expires) + Person::update( + conn, + blocked_person.id, + &PersonUpdateForm::builder() + .banned(Some(true)) + .ban_expires(Some(expires)) + .build(), + ) }) .await??; if self.remove_data.unwrap_or(false) { diff --git a/crates/apub/src/activities/block/undo_block_user.rs b/crates/apub/src/activities/block/undo_block_user.rs index ee6f631a..b93d0aa3 100644 --- a/crates/apub/src/activities/block/undo_block_user.rs +++ b/crates/apub/src/activities/block/undo_block_user.rs @@ -7,6 +7,8 @@ use crate::{ verify_is_public, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser}, @@ -24,7 +26,7 @@ use lemmy_db_schema::{ source::{ community::{CommunityPersonBan, CommunityPersonBanForm}, moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm}, - person::Person, + person::{Person, PersonUpdateForm}, }, traits::{Bannable, Crud}, }; @@ -90,6 +92,10 @@ impl ActivityHandler for UndoBlockUser { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; verify_domains_match(self.actor.inner(), self.object.actor.inner())?; self.object.verify(context, request_counter).await?; @@ -121,7 +127,14 @@ impl ActivityHandler for UndoBlockUser { { SiteOrCommunity::Site(_site) => { let blocked_person = blocking(context.pool(), move |conn| { - Person::ban_person(conn, blocked_person.id, false, expires) + Person::update( + conn, + blocked_person.id, + &PersonUpdateForm::builder() + .banned(Some(false)) + .ban_expires(Some(expires)) + .build(), + ) }) .await??; diff --git a/crates/apub/src/activities/community/add_mod.rs b/crates/apub/src/activities/community/add_mod.rs index 4dbd463b..86ee9166 100644 --- a/crates/apub/src/activities/community/add_mod.rs +++ b/crates/apub/src/activities/community/add_mod.rs @@ -12,6 +12,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, generate_moderators_url, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, @@ -84,6 +86,10 @@ impl ActivityHandler for AddMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index e5419680..2886c2bf 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -1,6 +1,8 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_is_public}, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, insert_activity, objects::community::ApubCommunity, protocol::{ @@ -11,6 +13,7 @@ use crate::{ }; use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler}; use activitystreams_kinds::{activity::AnnounceType, public}; +use lemmy_api_common::utils::blocking; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use tracing::debug; @@ -84,9 +87,13 @@ impl ActivityHandler for AnnounceActivity { #[tracing::instrument(skip_all)] async fn verify( &self, - _context: &Data, + context: &Data, _request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; Ok(()) } diff --git a/crates/apub/src/activities/community/remove_mod.rs b/crates/apub/src/activities/community/remove_mod.rs index ede6a008..3dca0239 100644 --- a/crates/apub/src/activities/community/remove_mod.rs +++ b/crates/apub/src/activities/community/remove_mod.rs @@ -12,6 +12,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, generate_moderators_url, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, @@ -84,6 +86,10 @@ impl ActivityHandler for RemoveMod { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; diff --git a/crates/apub/src/activities/community/report.rs b/crates/apub/src/activities/community/report.rs index c3b50abf..de3fb356 100644 --- a/crates/apub/src/activities/community/report.rs +++ b/crates/apub/src/activities/community/report.rs @@ -1,5 +1,7 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community}, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::community::report::Report, @@ -74,6 +76,10 @@ impl ActivityHandler for Report { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + let community = self.to[0] .dereference(context, local_instance(context), request_counter) .await?; diff --git a/crates/apub/src/activities/community/update.rs b/crates/apub/src/activities/community/update.rs index 57a31221..b444f4f5 100644 --- a/crates/apub/src/activities/community/update.rs +++ b/crates/apub/src/activities/community/update.rs @@ -7,6 +7,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::community::update::UpdateCommunity, @@ -19,10 +21,7 @@ use activitypub_federation::{ }; use activitystreams_kinds::{activity::UpdateType, public}; use lemmy_api_common::utils::blocking; -use lemmy_db_schema::{ - source::community::{Community, CommunityForm}, - traits::Crud, -}; +use lemmy_db_schema::{source::community::Community, traits::Crud}; use lemmy_utils::error::LemmyError; use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud}; use url::Url; @@ -72,6 +71,9 @@ impl ActivityHandler for UpdateCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_is_public(&self.to, &self.cc)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; @@ -101,19 +103,10 @@ impl ActivityHandler for UpdateCommunity { ) -> Result<(), LemmyError> { let community = self.get_community(context, request_counter).await?; - let updated_community = self.object.into_form(); - let cf = CommunityForm { - name: updated_community.name, - title: updated_community.title, - description: updated_community.description, - nsfw: updated_community.nsfw, - // TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours - icon: updated_community.icon, - banner: updated_community.banner, - ..CommunityForm::default() - }; + let community_update_form = self.object.into_update_form(); + let updated_community = blocking(context.pool(), move |conn| { - Community::update(conn, community.id, &cf) + Community::update(conn, community.id, &community_update_form) }) .await??; diff --git a/crates/apub/src/activities/create_or_update/comment.rs b/crates/apub/src/activities/create_or_update/comment.rs index dad6ada9..3e0de366 100644 --- a/crates/apub/src/activities/create_or_update/comment.rs +++ b/crates/apub/src/activities/create_or_update/comment.rs @@ -8,6 +8,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, mentions::MentionOrValue, objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson}, @@ -115,6 +117,10 @@ impl ActivityHandler for CreateOrUpdateComment { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_is_public(&self.to, &self.cc)?; let post = self.object.get_parents(context, request_counter).await?.0; let community = self.get_community(context, request_counter).await?; diff --git a/crates/apub/src/activities/create_or_update/post.rs b/crates/apub/src/activities/create_or_update/post.rs index d9d7b854..1f997f72 100644 --- a/crates/apub/src/activities/create_or_update/post.rs +++ b/crates/apub/src/activities/create_or_update/post.rs @@ -8,6 +8,8 @@ use crate::{ verify_person_in_community, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost}, protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType}, ActorType, @@ -93,6 +95,9 @@ impl ActivityHandler for CreateOrUpdatePost { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_is_public(&self.to, &self.cc)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; diff --git a/crates/apub/src/activities/create_or_update/private_message.rs b/crates/apub/src/activities/create_or_update/private_message.rs index 9ad54769..20310bc4 100644 --- a/crates/apub/src/activities/create_or_update/private_message.rs +++ b/crates/apub/src/activities/create_or_update/private_message.rs @@ -1,5 +1,7 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person}, + check_apub_id_valid, + fetch_local_site_data, objects::{person::ApubPerson, private_message::ApubPrivateMessage}, protocol::activities::{ create_or_update::private_message::CreateOrUpdatePrivateMessage, @@ -69,6 +71,10 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_person(&self.actor, context, request_counter).await?; verify_domains_match(self.actor.inner(), self.object.id.inner())?; verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?; diff --git a/crates/apub/src/activities/deletion/delete.rs b/crates/apub/src/activities/deletion/delete.rs index d2572590..95024c47 100644 --- a/crates/apub/src/activities/deletion/delete.rs +++ b/crates/apub/src/activities/deletion/delete.rs @@ -4,6 +4,8 @@ use crate::{ deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, generate_activity_id, }, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::{activities::deletion::delete::Delete, IdOrNestedObject}, @@ -14,8 +16,8 @@ use anyhow::anyhow; use lemmy_api_common::utils::blocking; use lemmy_db_schema::{ source::{ - comment::Comment, - community::Community, + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, moderator::{ ModRemoveComment, ModRemoveCommentForm, @@ -24,7 +26,7 @@ use lemmy_db_schema::{ ModRemovePost, ModRemovePostForm, }, - post::Post, + post::{Post, PostUpdateForm}, }, traits::Crud, }; @@ -55,6 +57,9 @@ impl ActivityHandler for Delete { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?; Ok(()) } @@ -150,7 +155,11 @@ pub(in crate::activities) async fn receive_remove_action( }) .await??; let deleted_community = blocking(context.pool(), move |conn| { - Community::update_removed(conn, community.id, true) + Community::update( + conn, + community.id, + &CommunityUpdateForm::builder().removed(Some(true)).build(), + ) }) .await??; @@ -168,7 +177,11 @@ pub(in crate::activities) async fn receive_remove_action( }) .await??; let removed_post = blocking(context.pool(), move |conn| { - Post::update_removed(conn, post.id, true) + Post::update( + conn, + post.id, + &PostUpdateForm::builder().removed(Some(true)).build(), + ) }) .await??; @@ -186,7 +199,11 @@ pub(in crate::activities) async fn receive_remove_action( }) .await??; let removed_comment = blocking(context.pool(), move |conn| { - Comment::update_removed(conn, comment.id, true) + Comment::update( + conn, + comment.id, + &CommentUpdateForm::builder().removed(Some(true)).build(), + ) }) .await??; diff --git a/crates/apub/src/activities/deletion/delete_user.rs b/crates/apub/src/activities/deletion/delete_user.rs index 9570d85d..5fb453ee 100644 --- a/crates/apub/src/activities/deletion/delete_user.rs +++ b/crates/apub/src/activities/deletion/delete_user.rs @@ -1,5 +1,7 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person}, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{instance::remote_instance_inboxes, person::ApubPerson}, protocol::activities::deletion::delete_user::DeleteUser, @@ -11,7 +13,7 @@ use activitypub_federation::{ utils::verify_urls_match, }; use activitystreams_kinds::{activity::DeleteType, public}; -use lemmy_api_common::utils::delete_user_account; +use lemmy_api_common::utils::{blocking, delete_user_account}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; @@ -36,6 +38,9 @@ impl ActivityHandler for DeleteUser { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_is_public(&self.to, &[])?; verify_person(&self.actor, context, request_counter).await?; verify_urls_match(self.actor.inner(), self.object.inner())?; diff --git a/crates/apub/src/activities/deletion/mod.rs b/crates/apub/src/activities/deletion/mod.rs index 87dcd2d8..0a131c40 100644 --- a/crates/apub/src/activities/deletion/mod.rs +++ b/crates/apub/src/activities/deletion/mod.rs @@ -28,11 +28,11 @@ use activitystreams_kinds::public; use lemmy_api_common::utils::blocking; use lemmy_db_schema::{ source::{ - comment::Comment, - community::Community, + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, person::Person, - post::Post, - private_message::PrivateMessage, + post::{Post, PostUpdateForm}, + private_message::{PrivateMessage, PrivateMessageUpdateForm}, }, traits::Crud, }; @@ -239,7 +239,13 @@ async fn receive_delete_action( } let community = blocking(context.pool(), move |conn| { - Community::update_deleted(conn, community.id, deleted) + Community::update( + conn, + community.id, + &CommunityUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) }) .await??; send_community_ws_message( @@ -254,7 +260,11 @@ async fn receive_delete_action( DeletableObjects::Post(post) => { if deleted != post.deleted { let deleted_post = blocking(context.pool(), move |conn| { - Post::update_deleted(conn, post.id, deleted) + Post::update( + conn, + post.id, + &PostUpdateForm::builder().deleted(Some(deleted)).build(), + ) }) .await??; send_post_ws_message( @@ -270,7 +280,11 @@ async fn receive_delete_action( DeletableObjects::Comment(comment) => { if deleted != comment.deleted { let deleted_comment = blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, comment.id, deleted) + Comment::update( + conn, + comment.id, + &CommentUpdateForm::builder().deleted(Some(deleted)).build(), + ) }) .await??; send_comment_ws_message_simple( @@ -283,7 +297,13 @@ async fn receive_delete_action( } DeletableObjects::PrivateMessage(pm) => { let deleted_private_message = blocking(context.pool(), move |conn| { - PrivateMessage::update_deleted(conn, pm.id, deleted) + PrivateMessage::update( + conn, + pm.id, + &PrivateMessageUpdateForm::builder() + .deleted(Some(deleted)) + .build(), + ) }) .await??; diff --git a/crates/apub/src/activities/deletion/undo_delete.rs b/crates/apub/src/activities/deletion/undo_delete.rs index c43e4d87..f73c780c 100644 --- a/crates/apub/src/activities/deletion/undo_delete.rs +++ b/crates/apub/src/activities/deletion/undo_delete.rs @@ -4,6 +4,8 @@ use crate::{ deletion::{receive_delete_action, verify_delete_activity, DeletableObjects}, generate_activity_id, }, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete}, @@ -13,8 +15,8 @@ use activitystreams_kinds::activity::UndoType; use lemmy_api_common::utils::blocking; use lemmy_db_schema::{ source::{ - comment::Comment, - community::Community, + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, moderator::{ ModRemoveComment, ModRemoveCommentForm, @@ -23,7 +25,7 @@ use lemmy_db_schema::{ ModRemovePost, ModRemovePostForm, }, - post::Post, + post::{Post, PostUpdateForm}, }, traits::Crud, }; @@ -54,6 +56,9 @@ impl ActivityHandler for UndoDelete { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; self.object.verify(context, request_counter).await?; verify_delete_activity( &self.object, @@ -148,7 +153,11 @@ impl UndoDelete { }) .await??; let deleted_community = blocking(context.pool(), move |conn| { - Community::update_removed(conn, community.id, false) + Community::update( + conn, + community.id, + &CommunityUpdateForm::builder().removed(Some(false)).build(), + ) }) .await??; send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?; @@ -165,7 +174,11 @@ impl UndoDelete { }) .await??; let removed_post = blocking(context.pool(), move |conn| { - Post::update_removed(conn, post.id, false) + Post::update( + conn, + post.id, + &PostUpdateForm::builder().removed(Some(false)).build(), + ) }) .await??; send_post_ws_message(removed_post.id, EditPost, None, None, context).await?; @@ -182,7 +195,11 @@ impl UndoDelete { }) .await??; let removed_comment = blocking(context.pool(), move |conn| { - Comment::update_removed(conn, comment.id, false) + Comment::update( + conn, + comment.id, + &CommentUpdateForm::builder().removed(Some(false)).build(), + ) }) .await??; send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?; diff --git a/crates/apub/src/activities/following/accept.rs b/crates/apub/src/activities/following/accept.rs index f964da08..c4bbbb1b 100644 --- a/crates/apub/src/activities/following/accept.rs +++ b/crates/apub/src/activities/following/accept.rs @@ -1,5 +1,7 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity}, + check_apub_id_valid, + fetch_local_site_data, local_instance, protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, ActorType, @@ -67,6 +69,10 @@ impl ActivityHandler for AcceptFollowCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; + verify_urls_match(self.actor.inner(), self.object.object.inner())?; self.object.verify(context, request_counter).await?; Ok(()) diff --git a/crates/apub/src/activities/following/follow.rs b/crates/apub/src/activities/following/follow.rs index b0ae72c7..512c074c 100644 --- a/crates/apub/src/activities/following/follow.rs +++ b/crates/apub/src/activities/following/follow.rs @@ -5,6 +5,8 @@ use crate::{ verify_person, verify_person_in_community, }, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity}, @@ -84,6 +86,9 @@ impl ActivityHandler for FollowCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_person(&self.actor, context, request_counter).await?; let community = self .object diff --git a/crates/apub/src/activities/following/undo_follow.rs b/crates/apub/src/activities/following/undo_follow.rs index 94b0b68e..b37e21fb 100644 --- a/crates/apub/src/activities/following/undo_follow.rs +++ b/crates/apub/src/activities/following/undo_follow.rs @@ -1,5 +1,7 @@ use crate::{ activities::{generate_activity_id, send_lemmy_activity, verify_person}, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity}, @@ -63,6 +65,9 @@ impl ActivityHandler for UndoFollowCommunity { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; verify_urls_match(self.actor.inner(), self.object.actor.inner())?; verify_person(&self.actor, context, request_counter).await?; self.object.verify(context, request_counter).await?; diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index 8205967f..0c013fbe 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -14,7 +14,10 @@ use activitypub_federation::{ use activitystreams_kinds::public; use anyhow::anyhow; use lemmy_api_common::utils::blocking; -use lemmy_db_schema::{newtypes::CommunityId, source::community::Community}; +use lemmy_db_schema::{ + newtypes::CommunityId, + source::{community::Community, local_site::LocalSite}, +}; use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; @@ -167,9 +170,14 @@ where ActorT: Actor + ActorType, Activity: ActivityHandler, { - if !context.settings().federation.enabled { + let federation_enabled = blocking(context.pool(), &LocalSite::read) + .await? + .map(|l| l.federation_enabled) + .unwrap_or(false); + if !federation_enabled { return Ok(()); } + info!("Sending activity {}", activity.id().to_string()); let activity = WithContext::new(activity, CONTEXT.deref().clone()); diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs index f6fc36a2..80e319bf 100644 --- a/crates/apub/src/activities/voting/undo_vote.rs +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -6,6 +6,8 @@ use crate::{ voting::{undo_vote_comment, undo_vote_post}, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::voting::{ @@ -82,6 +84,9 @@ impl ActivityHandler for UndoVote { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; verify_urls_match(self.actor.inner(), self.object.actor.inner())?; diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs index aafde574..7d07fe8c 100644 --- a/crates/apub/src/activities/voting/vote.rs +++ b/crates/apub/src/activities/voting/vote.rs @@ -6,6 +6,8 @@ use crate::{ voting::{vote_comment, vote_post}, }, activity_lists::AnnouncableActivities, + check_apub_id_valid, + fetch_local_site_data, local_instance, objects::{community::ApubCommunity, person::ApubPerson}, protocol::activities::voting::vote::{Vote, VoteType}, @@ -17,7 +19,7 @@ use anyhow::anyhow; use lemmy_api_common::utils::blocking; use lemmy_db_schema::{ newtypes::CommunityId, - source::{community::Community, post::Post, site::Site}, + source::{community::Community, local_site::LocalSite, post::Post}, traits::Crud, }; use lemmy_utils::error::LemmyError; @@ -81,10 +83,16 @@ impl ActivityHandler for Vote { context: &Data, request_counter: &mut i32, ) -> Result<(), LemmyError> { + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + check_apub_id_valid(self.id(), &local_site_data, context.settings()) + .map_err(LemmyError::from_message)?; let community = self.get_community(context, request_counter).await?; verify_person_in_community(&self.actor, &community, context, request_counter).await?; - let site = blocking(context.pool(), Site::read_local).await??; - if self.kind == VoteType::Dislike && !site.enable_downvotes { + let enable_downvotes = blocking(context.pool(), LocalSite::read) + .await? + .map(|l| l.enable_downvotes) + .unwrap_or(true); + if self.kind == VoteType::Dislike && !enable_downvotes { return Err(anyhow!("Downvotes disabled").into()); } Ok(()) diff --git a/crates/apub/src/collections/community_moderators.rs b/crates/apub/src/collections/community_moderators.rs index 9f4374db..3f88010d 100644 --- a/crates/apub/src/collections/community_moderators.rs +++ b/crates/apub/src/collections/community_moderators.rs @@ -152,7 +152,8 @@ mod tests { use lemmy_db_schema::{ source::{ community::Community, - person::{Person, PersonForm}, + instance::Instance, + person::{Person, PersonInsertForm}, site::Site, }, traits::Crud, @@ -168,11 +169,14 @@ mod tests { let community = parse_lemmy_community(&context).await; let community_id = community.id; - let old_mod = PersonForm { - name: "holly".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let old_mod = PersonInsertForm::builder() + .name("holly".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); + let old_mod = Person::create(conn, &old_mod).unwrap(); let community_moderator_form = CommunityModeratorForm { community_id: community.id, @@ -210,5 +214,6 @@ mod tests { Person::delete(conn, new_mod.id).unwrap(); Community::delete(conn, community_context.0.id).unwrap(); Site::delete(conn, site.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/apub/src/fetcher/webfinger.rs b/crates/apub/src/fetcher/webfinger.rs index 428c8d59..0dfdfc22 100644 --- a/crates/apub/src/fetcher/webfinger.rs +++ b/crates/apub/src/fetcher/webfinger.rs @@ -2,7 +2,8 @@ use crate::{local_instance, ActorType}; use activitypub_federation::{core::object_id::ObjectId, traits::ApubObject}; use anyhow::anyhow; use itertools::Itertools; -use lemmy_db_schema::newtypes::DbUrl; +use lemmy_api_common::utils::blocking; +use lemmy_db_schema::{newtypes::DbUrl, source::local_site::LocalSite}; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use serde::{Deserialize, Serialize}; @@ -47,8 +48,14 @@ where ); debug!("Fetching webfinger url: {}", &fetch_url); + let local_site = blocking(context.pool(), LocalSite::read).await?; + let http_fetch_retry_limit = local_site + .as_ref() + .map(|l| l.federation_http_fetch_retry_limit) + .unwrap_or(25); + *request_counter += 1; - if *request_counter > context.settings().federation.http_fetch_retry_limit { + if *request_counter > http_fetch_retry_limit { return Err(LemmyError::from_message("Request retry limit reached")); } diff --git a/crates/apub/src/http/routes.rs b/crates/apub/src/http/routes.rs index d90bf400..a588b312 100644 --- a/crates/apub/src/http/routes.rs +++ b/crates/apub/src/http/routes.rs @@ -19,51 +19,46 @@ use actix_web::{ web, }; use http_signature_normalization_actix::digest::middleware::VerifyDigest; -use lemmy_utils::settings::structs::Settings; use sha2::{Digest, Sha256}; -pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) { - if settings.federation.enabled { - println!("federation enabled, host is {}", settings.hostname); +pub fn config(cfg: &mut web::ServiceConfig) { + cfg + .route("/", web::get().to(get_apub_site_http)) + .route("/site_outbox", web::get().to(get_apub_site_outbox)) + .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 - .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.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)), - ); - } + 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 diff --git a/crates/apub/src/http/site.rs b/crates/apub/src/http/site.rs index 9d0fde73..503bc5a9 100644 --- a/crates/apub/src/http/site.rs +++ b/crates/apub/src/http/site.rs @@ -7,7 +7,7 @@ use crate::{ use activitypub_federation::{deser::context::WithContext, traits::ApubObject}; use actix_web::{web, HttpRequest, HttpResponse}; use lemmy_api_common::utils::blocking; -use lemmy_db_schema::source::site::Site; +use lemmy_db_views::structs::SiteView; use lemmy_utils::error::LemmyError; use lemmy_websocket::LemmyContext; use url::Url; @@ -15,7 +15,10 @@ use url::Url; pub(crate) async fn get_apub_site_http( context: web::Data, ) -> Result { - let site: ApubSite = blocking(context.pool(), Site::read_local).await??.into(); + let site: ApubSite = blocking(context.pool(), SiteView::read_local) + .await?? + .site + .into(); let apub = site.into_apub(&context).await?; Ok(create_apub_response(&apub)) diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index b594acad..471a7564 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -6,13 +6,14 @@ use activitypub_federation::{ LocalInstance, }; use anyhow::Context; +use diesel::PgConnection; use lemmy_api_common::utils::blocking; -use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool}; -use lemmy_utils::{ - error::LemmyError, - location_info, - settings::{structs::Settings, SETTINGS}, +use lemmy_db_schema::{ + newtypes::DbUrl, + source::{activity::Activity, instance::Instance, local_site::LocalSite}, + utils::DbPool, }; +use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings}; use lemmy_websocket::LemmyContext; use once_cell::sync::{Lazy, OnceCell}; use url::{ParseError, Url}; @@ -31,16 +32,35 @@ static CONTEXT: Lazy> = Lazy::new(|| { }); // TODO: store this in context? but its only used in this crate, no need to expose it elsewhere +// TODO this singleton needs to be redone to account for live data. fn local_instance(context: &LemmyContext) -> &'static LocalInstance { static LOCAL_INSTANCE: OnceCell = OnceCell::new(); LOCAL_INSTANCE.get_or_init(|| { + let conn = &mut context + .pool() + .get() + .expect("getting connection for LOCAL_INSTANCE init"); + // Local site may be missing + let local_site = &LocalSite::read(conn); + let worker_count = local_site + .as_ref() + .map(|l| l.federation_worker_count) + .unwrap_or(64) as u64; + let http_fetch_retry_limit = local_site + .as_ref() + .map(|l| l.federation_http_fetch_retry_limit) + .unwrap_or(25); + let federation_debug = local_site + .as_ref() + .map(|l| l.federation_debug) + .unwrap_or(true); + let settings = InstanceSettings::builder() - .http_fetch_retry_limit(context.settings().federation.http_fetch_retry_limit) - .worker_count(context.settings().federation.worker_count) - .debug(context.settings().federation.debug) + .http_fetch_retry_limit(http_fetch_retry_limit) + .worker_count(worker_count) + .debug(federation_debug) // TODO No idea why, but you can't pass context.settings() to the verify_url_function closure // without the value getting captured. - .verify_url_function(|url| check_apub_id_valid(url, &SETTINGS)) .http_signature_compat(true) .build() .expect("configure federation"); @@ -62,8 +82,13 @@ fn local_instance(context: &LemmyContext) -> &'static LocalInstance { /// /// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a /// post/comment in a local community. -#[tracing::instrument(skip(settings))] -fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'static str> { +#[tracing::instrument(skip(settings, local_site_data))] +// TODO This function needs to be called by incoming activities +fn check_apub_id_valid( + apub_id: &Url, + local_site_data: &LocalSiteData, + settings: &Settings, +) -> Result<(), &'static str> { let domain = apub_id.domain().expect("apud id has domain").to_string(); let local_instance = settings .get_hostname_without_port() @@ -72,7 +97,12 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati return Ok(()); } - if !settings.federation.enabled { + if !local_site_data + .local_site + .as_ref() + .map(|l| l.federation_enabled) + .unwrap_or(true) + { return Err("Federation disabled"); } @@ -80,13 +110,13 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati return Err("Invalid protocol scheme"); } - if let Some(blocked) = settings.to_owned().federation.blocked_instances { + if let Some(blocked) = local_site_data.blocked_instances.as_ref() { if blocked.contains(&domain) { return Err("Domain is blocked"); } } - if let Some(allowed) = settings.to_owned().federation.allowed_instances { + if let Some(allowed) = local_site_data.allowed_instances.as_ref() { if !allowed.contains(&domain) { return Err("Domain is not in allowlist"); } @@ -95,13 +125,40 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati Ok(()) } -#[tracing::instrument(skip(settings))] +#[derive(Clone)] +pub(crate) struct LocalSiteData { + local_site: Option, + allowed_instances: Option>, + blocked_instances: Option>, +} + +pub(crate) fn fetch_local_site_data( + conn: &mut PgConnection, +) -> Result { + // LocalSite may be missing + let local_site = LocalSite::read(conn).ok(); + let allowed = Instance::allowlist(conn)?; + let blocked = Instance::blocklist(conn)?; + + // These can return empty vectors, so convert them to options + let allowed_instances = (!allowed.is_empty()).then(|| allowed); + let blocked_instances = (!blocked.is_empty()).then(|| blocked); + + Ok(LocalSiteData { + local_site, + allowed_instances, + blocked_instances, + }) +} + +#[tracing::instrument(skip(settings, local_site_data))] pub(crate) fn check_apub_id_valid_with_strictness( apub_id: &Url, is_strict: bool, + local_site_data: &LocalSiteData, settings: &Settings, ) -> Result<(), LemmyError> { - check_apub_id_valid(apub_id, settings).map_err(LemmyError::from_message)?; + check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?; let domain = apub_id.domain().expect("apud id has domain").to_string(); let local_instance = settings .get_hostname_without_port() @@ -110,15 +167,20 @@ pub(crate) fn check_apub_id_valid_with_strictness( return Ok(()); } - if let Some(mut allowed) = settings.to_owned().federation.allowed_instances { + if let Some(allowed) = local_site_data.allowed_instances.as_ref() { // Only check allowlist if this is a community, or strict allowlist is enabled. - let strict_allowlist = settings.to_owned().federation.strict_allowlist; + let strict_allowlist = local_site_data + .local_site + .as_ref() + .map(|l| l.federation_strict_allowlist) + .unwrap_or(true); if is_strict || strict_allowlist { // need to allow this explicitly because apub receive might contain objects from our local // instance. - allowed.push(local_instance); + let mut allowed_and_local = allowed.to_owned(); + allowed_and_local.push(local_instance); - if !allowed.contains(&domain) { + if !allowed_and_local.contains(&domain) { return Err(LemmyError::from_message( "Federation forbidden by strict allowlist", )); @@ -203,7 +265,7 @@ async fn insert_activity( let ap_id = ap_id.to_owned().into(); Ok( blocking(pool, move |conn| { - Activity::insert(conn, ap_id, activity, local, sensitive) + Activity::insert(conn, ap_id, activity, local, Some(sensitive)) }) .await??, ) diff --git a/crates/apub/src/objects/comment.rs b/crates/apub/src/objects/comment.rs index e292d2b6..154bd41c 100644 --- a/crates/apub/src/objects/comment.rs +++ b/crates/apub/src/objects/comment.rs @@ -1,6 +1,7 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, check_apub_id_valid_with_strictness, + fetch_local_site_data, local_instance, mentions::collect_non_local_mentions, objects::{read_from_string_or_source, verify_is_remote_object}, @@ -18,11 +19,12 @@ use activitypub_federation::{ }; use activitystreams_kinds::{object::NoteType, public}; use chrono::NaiveDateTime; -use lemmy_api_common::utils::blocking; +use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex}; use lemmy_db_schema::{ source::{ - comment::{Comment, CommentForm}, + comment::{Comment, CommentInsertForm, CommentUpdateForm}, community::Community, + local_site::LocalSite, person::Person, post::Post, }, @@ -81,7 +83,8 @@ impl ApubObject for ApubComment { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { if !self.deleted { blocking(context.pool(), move |conn| { - Comment::update_deleted(conn, self.id, true) + let form = CommentUpdateForm::builder().deleted(Some(true)).build(); + Comment::update(conn, self.id, &form) }) .await??; } @@ -148,7 +151,14 @@ impl ApubObject for ApubComment { Community::read(conn, community_id) }) .await??; - check_apub_id_valid_with_strictness(note.id.inner(), community.local, context.settings())?; + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + + check_apub_id_valid_with_strictness( + note.id.inner(), + community.local, + &local_site_data, + context.settings(), + )?; verify_is_remote_object(note.id.inner(), context.settings())?; verify_person_in_community( ¬e.attributed_to, @@ -179,10 +189,13 @@ impl ApubObject for ApubComment { let (post, parent_comment) = note.get_parents(context, request_counter).await?; let content = read_from_string_or_source(¬e.content, ¬e.media_type, ¬e.source); - let content_slurs_removed = remove_slurs(&content, &context.settings().slur_regex()); + + let local_site = blocking(context.pool(), LocalSite::read).await?.ok(); + let slur_regex = &local_site_opt_to_slur_regex(&local_site); + let content_slurs_removed = remove_slurs(&content, slur_regex); let language_id = LanguageTag::to_language_id_single(note.language, context.pool()).await?; - let form = CommentForm { + let form = CommentInsertForm { creator_id: creator.id, post_id: post.id, content: content_slurs_removed, @@ -244,6 +257,7 @@ pub(crate) mod tests { Community::delete(conn, data.1.id).unwrap(); Person::delete(conn, data.0.id).unwrap(); Site::delete(conn, data.3.id).unwrap(); + LocalSite::delete(conn).unwrap(); } #[actix_rt::test] diff --git a/crates/apub/src/objects/community.rs b/crates/apub/src/objects/community.rs index 9793c4fd..6e04f2c1 100644 --- a/crates/apub/src/objects/community.rs +++ b/crates/apub/src/objects/community.rs @@ -1,6 +1,7 @@ use crate::{ check_apub_id_valid_with_strictness, collections::{community_moderators::ApubCommunityModerators, CommunityContext}, + fetch_local_site_data, generate_moderators_url, generate_outbox_url, local_instance, @@ -21,8 +22,12 @@ use chrono::NaiveDateTime; use itertools::Itertools; use lemmy_api_common::utils::blocking; use lemmy_db_schema::{ - source::{actor_language::CommunityLanguage, community::Community}, - traits::ApubActor, + source::{ + actor_language::CommunityLanguage, + community::{Community, CommunityUpdateForm}, + instance::Instance, + }, + traits::{ApubActor, Crud}, }; use lemmy_db_views_actor::structs::CommunityFollowerView; use lemmy_utils::{ @@ -78,7 +83,8 @@ impl ApubObject for ApubCommunity { #[tracing::instrument(skip_all)] async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { blocking(context.pool(), move |conn| { - Community::update_deleted(conn, self.id, true) + let form = CommunityUpdateForm::builder().deleted(Some(true)).build(); + Community::update(conn, self.id, &form) }) .await??; Ok(()) @@ -138,11 +144,17 @@ impl ApubObject for ApubCommunity { context: &LemmyContext, request_counter: &mut i32, ) -> Result { - let form = Group::into_form(group.clone()); + let apub_id = group.id.inner().to_owned(); + let instance = blocking(context.pool(), move |conn| { + Instance::create_from_actor_id(conn, &apub_id) + }) + .await??; + + let form = Group::into_insert_form(group.clone(), instance.id); let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?; let community: ApubCommunity = blocking(context.pool(), move |conn| { - let community = Community::upsert(conn, &form)?; + let community = Community::create(conn, &form)?; CommunityLanguage::update(conn, languages, community.id)?; Ok::(community) }) @@ -205,6 +217,7 @@ impl ApubCommunity { ) -> Result, LemmyError> { let id = self.id; + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; let follows = blocking(context.pool(), move |conn| { CommunityFollowerView::for_community(conn, id) }) @@ -221,7 +234,10 @@ impl ApubCommunity { .unique() .filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname)) // Don't send to blocked instances - .filter(|inbox| check_apub_id_valid_with_strictness(inbox, false, context.settings()).is_ok()) + .filter(|inbox| { + check_apub_id_valid_with_strictness(inbox, false, &local_site_data, context.settings()) + .is_ok() + }) .collect(); Ok(inboxes) diff --git a/crates/apub/src/objects/instance.rs b/crates/apub/src/objects/instance.rs index ef4328ef..521de64f 100644 --- a/crates/apub/src/objects/instance.rs +++ b/crates/apub/src/objects/instance.rs @@ -1,5 +1,6 @@ use crate::{ check_apub_id_valid_with_strictness, + fetch_local_site_data, local_instance, objects::read_from_string_or_source_opt, protocol::{ @@ -19,12 +20,14 @@ use activitypub_federation::{ utils::verify_domains_match, }; use chrono::NaiveDateTime; -use lemmy_api_common::utils::blocking; +use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex}; use lemmy_db_schema::{ source::{ actor_language::SiteLanguage, - site::{Site, SiteForm}, + instance::Instance as DbInstance, + site::{Site, SiteInsertForm}, }, + traits::Crud, utils::{naive_now, DbPool}, }; use lemmy_utils::{ @@ -114,10 +117,13 @@ impl ApubObject for ApubSite { data: &Self::DataType, _request_counter: &mut i32, ) -> Result<(), LemmyError> { - check_apub_id_valid_with_strictness(apub.id.inner(), true, data.settings())?; + let local_site_data = blocking(data.pool(), fetch_local_site_data).await??; + + check_apub_id_valid_with_strictness(apub.id.inner(), true, &local_site_data, data.settings())?; verify_domains_match(expected_domain, apub.id.inner())?; - let slur_regex = &data.settings().slur_regex(); + let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); + check_slurs(&apub.name, slur_regex)?; check_slurs_opt(&apub.summary, slur_regex)?; Ok(()) @@ -129,27 +135,30 @@ impl ApubObject for ApubSite { data: &Self::DataType, _request_counter: &mut i32, ) -> Result { - let site_form = SiteForm { + let apub_id = apub.id.inner().to_owned(); + let instance = blocking(data.pool(), move |conn| { + DbInstance::create_from_actor_id(conn, &apub_id) + }) + .await??; + + let site_form = SiteInsertForm { name: apub.name.clone(), - sidebar: Some(read_from_string_or_source_opt( - &apub.content, - &None, - &apub.source, - )), + sidebar: read_from_string_or_source_opt(&apub.content, &None, &apub.source), updated: apub.updated.map(|u| u.clone().naive_local()), - icon: Some(apub.icon.clone().map(|i| i.url.into())), - banner: Some(apub.image.clone().map(|i| i.url.into())), - description: Some(apub.summary.clone()), + icon: apub.icon.clone().map(|i| i.url.into()), + banner: apub.image.clone().map(|i| i.url.into()), + description: apub.summary.clone(), actor_id: Some(apub.id.clone().into()), last_refreshed_at: Some(naive_now()), inbox_url: Some(apub.inbox.clone().into()), public_key: Some(apub.public_key.public_key_pem.clone()), - ..SiteForm::default() + private_key: None, + instance_id: instance.id, }; let languages = LanguageTag::to_language_id_multiple(apub.language, data.pool()).await?; let site = blocking(data.pool(), move |conn| { - let site = Site::upsert(conn, &site_form)?; + let site = Site::create(conn, &site_form)?; SiteLanguage::update(conn, languages, site.id)?; Ok::(site) }) diff --git a/crates/apub/src/objects/mod.rs b/crates/apub/src/objects/mod.rs index 8f11f03b..f54d0756 100644 --- a/crates/apub/src/objects/mod.rs +++ b/crates/apub/src/objects/mod.rs @@ -67,7 +67,7 @@ pub(crate) mod tests { }; use lemmy_utils::{ error::LemmyError, - rate_limit::{rate_limiter::RateLimiter, RateLimit}, + rate_limit::{rate_limiter::RateLimiter, RateLimit, RateLimitConfig}, settings::SETTINGS, }; use lemmy_websocket::{chat_server::ChatServer, LemmyContext}; @@ -96,10 +96,6 @@ pub(crate) mod tests { // call this to run migrations establish_unpooled_connection(); let settings = SETTINGS.to_owned(); - let rate_limiter = RateLimit { - rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), - rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(), - }; let client = Client::builder() .user_agent(build_user_agent(&settings)) .build() @@ -122,6 +118,14 @@ pub(crate) mod tests { async fn x() -> Result { Ok("".to_string()) } + + let rate_limit_config = RateLimitConfig::builder().build(); + + let rate_limiter = RateLimit { + rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), + rate_limit_config, + }; + let chat_server = ChatServer::startup( pool.clone(), rate_limiter, diff --git a/crates/apub/src/objects/person.rs b/crates/apub/src/objects/person.rs index b1a892a3..e496da07 100644 --- a/crates/apub/src/objects/person.rs +++ b/crates/apub/src/objects/person.rs @@ -1,5 +1,6 @@ use crate::{ check_apub_id_valid_with_strictness, + fetch_local_site_data, generate_outbox_url, objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt}, protocol::{ @@ -18,10 +19,13 @@ use activitypub_federation::{ utils::verify_domains_match, }; use chrono::NaiveDateTime; -use lemmy_api_common::utils::blocking; +use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex}; use lemmy_db_schema::{ - source::person::{Person as DbPerson, PersonForm}, - traits::ApubActor, + source::{ + instance::Instance, + person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm}, + }, + traits::{ApubActor, Crud}, utils::naive_now, }; use lemmy_utils::{ @@ -76,7 +80,8 @@ impl ApubObject for ApubPerson { #[tracing::instrument(skip_all)] async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { blocking(context.pool(), move |conn| { - DbPerson::update_deleted(conn, self.id, true) + let form = PersonUpdateForm::builder().deleted(Some(true)).build(); + DbPerson::update(conn, self.id, &form) }) .await??; Ok(()) @@ -119,12 +124,20 @@ impl ApubObject for ApubPerson { context: &LemmyContext, _request_counter: &mut i32, ) -> Result<(), LemmyError> { - verify_domains_match(person.id.inner(), expected_domain)?; - check_apub_id_valid_with_strictness(person.id.inner(), false, context.settings())?; + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); - let slur_regex = &context.settings().slur_regex(); check_slurs(&person.preferred_username, slur_regex)?; check_slurs_opt(&person.name, slur_regex)?; + + verify_domains_match(person.id.inner(), expected_domain)?; + check_apub_id_valid_with_strictness( + person.id.inner(), + false, + &local_site_data, + context.settings(), + )?; + let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source); check_slurs_opt(&bio, slur_regex)?; Ok(()) @@ -136,34 +149,37 @@ impl ApubObject for ApubPerson { context: &LemmyContext, request_counter: &mut i32, ) -> Result { - let person_form = PersonForm { + let apub_id = person.id.inner().to_owned(); + let instance = blocking(context.pool(), move |conn| { + Instance::create_from_actor_id(conn, &apub_id) + }) + .await??; + + let person_form = PersonInsertForm { name: person.preferred_username, - display_name: Some(person.name), + display_name: person.name, banned: None, ban_expires: None, deleted: None, - avatar: Some(person.icon.map(|i| i.url.into())), - banner: Some(person.image.map(|i| i.url.into())), + avatar: person.icon.map(|i| i.url.into()), + banner: person.image.map(|i| i.url.into()), published: person.published.map(|u| u.naive_local()), updated: person.updated.map(|u| u.naive_local()), actor_id: Some(person.id.into()), - bio: Some(read_from_string_or_source_opt( - &person.summary, - &None, - &person.source, - )), + bio: read_from_string_or_source_opt(&person.summary, &None, &person.source), local: Some(false), admin: Some(false), bot_account: Some(person.kind == UserTypes::Service), private_key: None, - public_key: Some(person.public_key.public_key_pem), + public_key: person.public_key.public_key_pem, last_refreshed_at: Some(naive_now()), inbox_url: Some(person.inbox.into()), - shared_inbox_url: Some(person.endpoints.map(|e| e.shared_inbox.into())), - matrix_user_id: Some(person.matrix_user_id), + shared_inbox_url: person.endpoints.map(|e| e.shared_inbox.into()), + matrix_user_id: person.matrix_user_id, + instance_id: instance.id, }; let person = blocking(context.pool(), move |conn| { - DbPerson::upsert(conn, &person_form) + DbPerson::create(conn, &person_form) }) .await??; @@ -230,22 +246,19 @@ pub(crate) mod tests { #[serial] async fn test_parse_lemmy_person() { let context = init_context(); - let conn = &mut context.pool().get().unwrap(); let (person, site) = parse_lemmy_person(&context).await; assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string())); assert!(!person.local); assert_eq!(person.bio.as_ref().unwrap().len(), 39); - DbPerson::delete(conn, person.id).unwrap(); - Site::delete(conn, site.id).unwrap(); + cleanup((person, site), &context); } #[actix_rt::test] #[serial] async fn test_parse_pleroma_person() { let context = init_context(); - let conn = &mut context.pool().get().unwrap(); // create and parse a fake pleroma instance actor, to avoid network request during test let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap(); @@ -272,7 +285,12 @@ pub(crate) mod tests { assert_eq!(request_counter, 0); assert_eq!(person.bio.as_ref().unwrap().len(), 873); - DbPerson::delete(conn, person.id).unwrap(); - Site::delete(conn, site.id).unwrap(); + cleanup((person, site), &context); + } + + fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) { + let conn = &mut context.pool().get().unwrap(); + DbPerson::delete(conn, data.0.id).unwrap(); + Site::delete(conn, data.1.id).unwrap(); } } diff --git a/crates/apub/src/objects/post.rs b/crates/apub/src/objects/post.rs index 655f0342..2f6f628c 100644 --- a/crates/apub/src/objects/post.rs +++ b/crates/apub/src/objects/post.rs @@ -1,6 +1,7 @@ use crate::{ activities::{verify_is_public, verify_person_in_community}, check_apub_id_valid_with_strictness, + fetch_local_site_data, local_instance, objects::{read_from_string_or_source_opt, verify_is_remote_object}, protocol::{ @@ -20,14 +21,18 @@ use activitypub_federation::{ }; use activitystreams_kinds::public; use chrono::NaiveDateTime; -use lemmy_api_common::{request::fetch_site_data, utils::blocking}; +use lemmy_api_common::{ + request::fetch_site_data, + utils::{blocking, local_site_opt_to_slur_regex}, +}; use lemmy_db_schema::{ self, source::{ community::Community, + local_site::LocalSite, moderator::{ModLockPost, ModLockPostForm, ModStickyPost, ModStickyPostForm}, person::Person, - post::{Post, PostForm}, + post::{Post, PostInsertForm, PostUpdateForm}, }, traits::Crud, }; @@ -84,7 +89,8 @@ impl ApubObject for ApubPost { async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> { if !self.deleted { blocking(context.pool(), move |conn| { - Post::update_deleted(conn, self.id, true) + let form = PostUpdateForm::builder().deleted(Some(true)).build(); + Post::update(conn, self.id, &form) }) .await??; } @@ -140,10 +146,20 @@ impl ApubObject for ApubPost { verify_is_remote_object(page.id.inner(), context.settings())?; }; + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + let community = page.extract_community(context, request_counter).await?; - check_apub_id_valid_with_strictness(page.id.inner(), community.local, context.settings())?; + check_apub_id_valid_with_strictness( + page.id.inner(), + community.local, + &local_site_data, + context.settings(), + )?; verify_person_in_community(&page.creator()?, &community, context, request_counter).await?; - check_slurs(&page.name, &context.settings().slur_regex())?; + + let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); + check_slurs(&page.name, slur_regex)?; + verify_domains_match(page.creator()?.inner(), page.id.inner())?; verify_is_public(&page.to, &page.cc)?; Ok(()) @@ -181,16 +197,19 @@ impl ApubObject for ApubPost { (None, page.image.map(|i| i.url.into())) }; let (embed_title, embed_description, embed_video_url) = metadata_res - .map(|u| (Some(u.title), Some(u.description), Some(u.embed_video_url))) + .map(|u| (u.title, u.description, u.embed_video_url)) .unwrap_or_default(); + let local_site = blocking(context.pool(), LocalSite::read).await?.ok(); + let slur_regex = &local_site_opt_to_slur_regex(&local_site); + let body_slurs_removed = read_from_string_or_source_opt(&page.content, &page.media_type, &page.source) - .map(|s| Some(remove_slurs(&s, &context.settings().slur_regex()))); + .map(|s| remove_slurs(&s, slur_regex)); let language_id = LanguageTag::to_language_id_single(page.language, context.pool()).await?; - PostForm { + PostInsertForm { name: page.name.clone(), - url: Some(url.map(Into::into)), + url: url.map(Into::into), body: body_slurs_removed, creator_id: creator.id, community_id: community.id, @@ -204,23 +223,22 @@ impl ApubObject for ApubPost { embed_title, embed_description, embed_video_url, - thumbnail_url: Some(thumbnail_url), + thumbnail_url, ap_id: Some(page.id.clone().into()), local: Some(false), language_id, } } else { // if is mod action, only update locked/stickied fields, nothing else - PostForm { - name: page.name.clone(), - creator_id: creator.id, - community_id: community.id, - locked: page.comments_enabled.map(|e| !e), - stickied: page.stickied, - updated: page.updated.map(|u| u.naive_local()), - ap_id: Some(page.id.clone().into()), - ..Default::default() - } + PostInsertForm::builder() + .name(page.name.clone()) + .creator_id(creator.id) + .community_id(community.id) + .ap_id(Some(page.id.clone().into())) + .locked(page.comments_enabled.map(|e| !e)) + .stickied(page.stickied) + .updated(page.updated.map(|u| u.naive_local())) + .build() }; // read existing, local post if any (for generating mod log) @@ -228,7 +246,7 @@ impl ApubObject for ApubPost { .dereference_local(context) .await; - let post = blocking(context.pool(), move |conn| Post::upsert(conn, &form)).await??; + let post = blocking(context.pool(), move |conn| Post::create(conn, &form)).await??; // write mod log entries for sticky/lock if Page::is_stickied_changed(&old_post, &page.stickied) { diff --git a/crates/apub/src/objects/private_message.rs b/crates/apub/src/objects/private_message.rs index 3aef60c6..bdab7dd0 100644 --- a/crates/apub/src/objects/private_message.rs +++ b/crates/apub/src/objects/private_message.rs @@ -1,5 +1,6 @@ use crate::{ check_apub_id_valid_with_strictness, + fetch_local_site_data, local_instance, objects::read_from_string_or_source, protocol::{ @@ -18,7 +19,7 @@ use lemmy_api_common::utils::{blocking, check_person_block}; use lemmy_db_schema::{ source::{ person::Person, - private_message::{PrivateMessage, PrivateMessageForm}, + private_message::{PrivateMessage, PrivateMessageInsertForm}, }, traits::Crud, }; @@ -108,7 +109,15 @@ impl ApubObject for ApubPrivateMessage { ) -> Result<(), LemmyError> { verify_domains_match(note.id.inner(), expected_domain)?; verify_domains_match(note.attributed_to.inner(), note.id.inner())?; - check_apub_id_valid_with_strictness(note.id.inner(), false, context.settings())?; + + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + + check_apub_id_valid_with_strictness( + note.id.inner(), + false, + &local_site_data, + context.settings(), + )?; let person = note .attributed_to .dereference(context, local_instance(context), request_counter) @@ -134,7 +143,7 @@ impl ApubObject for ApubPrivateMessage { .await?; check_person_block(creator.id, recipient.id, context.pool()).await?; - let form = PrivateMessageForm { + let form = PrivateMessageInsertForm { creator_id: creator.id, recipient_id: recipient.id, content: read_from_string_or_source(¬e.content, &None, ¬e.source), @@ -146,7 +155,7 @@ impl ApubObject for ApubPrivateMessage { local: Some(false), }; let pm = blocking(context.pool(), move |conn| { - PrivateMessage::upsert(conn, &form) + PrivateMessage::create(conn, &form) }) .await??; Ok(pm.into()) diff --git a/crates/apub/src/protocol/objects/group.rs b/crates/apub/src/protocol/objects/group.rs index f6c8f517..5abfa2b2 100644 --- a/crates/apub/src/protocol/objects/group.rs +++ b/crates/apub/src/protocol/objects/group.rs @@ -4,6 +4,7 @@ use crate::{ community_moderators::ApubCommunityModerators, community_outbox::ApubCommunityOutbox, }, + fetch_local_site_data, objects::{community::ApubCommunity, read_from_string_or_source_opt}, protocol::{ objects::{Endpoints, LanguageTag}, @@ -18,7 +19,12 @@ use activitypub_federation::{ }; use activitystreams_kinds::actor::GroupType; use chrono::{DateTime, FixedOffset}; -use lemmy_db_schema::{source::community::CommunityForm, utils::naive_now}; +use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex}; +use lemmy_db_schema::{ + newtypes::InstanceId, + source::community::{CommunityInsertForm, CommunityUpdateForm}, + utils::naive_now, +}; use lemmy_utils::{ error::LemmyError, utils::{check_slurs, check_slurs_opt}, @@ -69,10 +75,18 @@ impl Group { expected_domain: &Url, context: &LemmyContext, ) -> Result<(), LemmyError> { - check_apub_id_valid_with_strictness(self.id.inner(), true, context.settings())?; + let local_site_data = blocking(context.pool(), fetch_local_site_data).await??; + + check_apub_id_valid_with_strictness( + self.id.inner(), + true, + &local_site_data, + context.settings(), + )?; verify_domains_match(expected_domain, self.id.inner())?; - let slur_regex = &context.settings().slur_regex(); + let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site); + check_slurs(&self.preferred_username, slur_regex)?; check_slurs_opt(&self.name, slur_regex)?; let description = read_from_string_or_source_opt(&self.summary, &None, &self.source); @@ -80,10 +94,35 @@ impl Group { Ok(()) } - pub(crate) fn into_form(self) -> CommunityForm { - CommunityForm { + pub(crate) fn into_insert_form(self, instance_id: InstanceId) -> CommunityInsertForm { + CommunityInsertForm { name: self.preferred_username.clone(), title: self.name.unwrap_or(self.preferred_username), + description: read_from_string_or_source_opt(&self.summary, &None, &self.source), + removed: None, + published: self.published.map(|u| u.naive_local()), + updated: self.updated.map(|u| u.naive_local()), + deleted: None, + nsfw: Some(self.sensitive.unwrap_or(false)), + actor_id: Some(self.id.into()), + local: Some(false), + private_key: None, + hidden: Some(false), + public_key: self.public_key.public_key_pem, + last_refreshed_at: Some(naive_now()), + icon: self.icon.map(|i| i.url.into()), + banner: self.image.map(|i| i.url.into()), + followers_url: Some(self.followers.into()), + inbox_url: Some(self.inbox.into()), + shared_inbox_url: self.endpoints.map(|e| e.shared_inbox.into()), + posting_restricted_to_mods: self.posting_restricted_to_mods, + instance_id, + } + } + + pub(crate) fn into_update_form(self) -> CommunityUpdateForm { + CommunityUpdateForm { + title: Some(self.name.unwrap_or(self.preferred_username)), description: Some(read_from_string_or_source_opt( &self.summary, &None, @@ -91,7 +130,7 @@ impl Group { )), removed: None, published: self.published.map(|u| u.naive_local()), - updated: self.updated.map(|u| u.naive_local()), + updated: Some(self.updated.map(|u| u.naive_local())), deleted: None, nsfw: Some(self.sensitive.unwrap_or(false)), actor_id: Some(self.id.into()), diff --git a/crates/db_schema/Cargo.toml b/crates/db_schema/Cargo.toml index ea8d3440..155d8440 100644 --- a/crates/db_schema/Cargo.toml +++ b/crates/db_schema/Cargo.toml @@ -34,6 +34,8 @@ sha2 = { version = "0.10.6", optional = true } regex = { version = "1.6.0", optional = true } once_cell = { version = "1.15.0", optional = true } diesel_ltree = "0.3.0" +typed-builder = "0.10.0" [dev-dependencies] serial_test = "0.9.0" + diff --git a/crates/db_schema/src/aggregates/comment_aggregates.rs b/crates/db_schema/src/aggregates/comment_aggregates.rs index c24e0b94..1130d910 100644 --- a/crates/db_schema/src/aggregates/comment_aggregates.rs +++ b/crates/db_schema/src/aggregates/comment_aggregates.rs @@ -18,10 +18,11 @@ mod tests { use crate::{ aggregates::comment_aggregates::CommentAggregates, source::{ - comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, - post::{Post, PostForm}, + comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm}, }, traits::{Crud, Likeable}, utils::establish_unpooled_connection, @@ -33,55 +34,54 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy_comment_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy_comment_agg".into()) + .public_key("pubkey".into()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let another_person = PersonForm { - name: "jerry_comment_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let another_person = PersonInsertForm::builder() + .name("jerry_comment_agg".into()) + .public_key("pubkey".into()) + .instance_id(inserted_instance.id) + .build(); let another_inserted_person = Person::create(conn, &another_person).unwrap(); - let new_community = CommunityForm { - name: "TIL_comment_agg".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL_comment_agg".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let child_comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let _inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); @@ -139,5 +139,7 @@ mod tests { // Delete the community let community_num_deleted = Community::delete(conn, inserted_community.id).unwrap(); assert_eq!(1, community_num_deleted); + + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_schema/src/aggregates/community_aggregates.rs b/crates/db_schema/src/aggregates/community_aggregates.rs index 9dfa710f..1a2c4d24 100644 --- a/crates/db_schema/src/aggregates/community_aggregates.rs +++ b/crates/db_schema/src/aggregates/community_aggregates.rs @@ -18,10 +18,11 @@ mod tests { use crate::{ aggregates::community_aggregates::CommunityAggregates, source::{ - comment::{Comment, CommentForm}, - community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm}, - person::{Person, PersonForm}, - post::{Post, PostForm}, + comment::{Comment, CommentInsertForm}, + community::{Community, CommunityFollower, CommunityFollowerForm, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm}, }, traits::{Crud, Followable}, utils::establish_unpooled_connection, @@ -33,37 +34,39 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy_community_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy_community_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let another_person = PersonForm { - name: "jerry_community_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let another_person = PersonInsertForm::builder() + .name("jerry_community_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let another_inserted_person = Person::create(conn, &another_person).unwrap(); - let new_community = CommunityForm { - name: "TIL_community_agg".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL_community_agg".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let another_community = CommunityForm { - name: "TIL_community_agg_2".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let another_community = CommunityInsertForm::builder() + .name("TIL_community_agg_2".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let another_inserted_community = Community::create(conn, &another_community).unwrap(); @@ -91,30 +94,27 @@ mod tests { CommunityFollower::follow(conn, &another_community_follow).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let child_comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let _inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); diff --git a/crates/db_schema/src/aggregates/person_aggregates.rs b/crates/db_schema/src/aggregates/person_aggregates.rs index ff0cb843..e9417efb 100644 --- a/crates/db_schema/src/aggregates/person_aggregates.rs +++ b/crates/db_schema/src/aggregates/person_aggregates.rs @@ -14,10 +14,11 @@ mod tests { use crate::{ aggregates::person_aggregates::PersonAggregates, source::{ - comment::{Comment, CommentForm, CommentLike, CommentLikeForm}, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, - post::{Post, PostForm, PostLike, PostLikeForm}, + comment::{Comment, CommentInsertForm, CommentLike, CommentLikeForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm, PostLike, PostLikeForm}, }, traits::{Crud, Likeable}, utils::establish_unpooled_connection, @@ -29,37 +30,38 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy_user_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy_user_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let another_person = PersonForm { - name: "jerry_user_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let another_person = PersonInsertForm::builder() + .name("jerry_user_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let another_inserted_person = Person::create(conn, &another_person).unwrap(); - let new_community = CommunityForm { - name: "TIL_site_agg".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL_site_agg".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); @@ -71,12 +73,11 @@ mod tests { let _inserted_post_like = PostLike::like(conn, &post_like).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); @@ -89,12 +90,11 @@ mod tests { let _inserted_comment_like = CommentLike::like(conn, &comment_like).unwrap(); - let child_comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); @@ -156,5 +156,7 @@ mod tests { // Should be none found let after_delete = PersonAggregates::read(conn, inserted_person.id); assert!(after_delete.is_err()); + + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_schema/src/aggregates/post_aggregates.rs b/crates/db_schema/src/aggregates/post_aggregates.rs index 1782d4d6..ac7c0eac 100644 --- a/crates/db_schema/src/aggregates/post_aggregates.rs +++ b/crates/db_schema/src/aggregates/post_aggregates.rs @@ -14,10 +14,11 @@ mod tests { use crate::{ aggregates::post_aggregates::PostAggregates, source::{ - comment::{Comment, CommentForm}, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, - post::{Post, PostForm, PostLike, PostLikeForm}, + comment::{Comment, CommentInsertForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm, PostLike, PostLikeForm}, }, traits::{Crud, Likeable}, utils::establish_unpooled_connection, @@ -29,55 +30,54 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy_community_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy_community_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let another_person = PersonForm { - name: "jerry_community_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let another_person = PersonInsertForm::builder() + .name("jerry_community_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let another_inserted_person = Person::create(conn, &another_person).unwrap(); - let new_community = CommunityForm { - name: "TIL_community_agg".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL_community_agg".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let child_comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); @@ -142,5 +142,7 @@ mod tests { // Should be none found, since the creator was deleted let after_delete = PostAggregates::read(conn, inserted_post.id); assert!(after_delete.is_err()); + + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_schema/src/aggregates/site_aggregates.rs b/crates/db_schema/src/aggregates/site_aggregates.rs index ed269a8b..41e82c95 100644 --- a/crates/db_schema/src/aggregates/site_aggregates.rs +++ b/crates/db_schema/src/aggregates/site_aggregates.rs @@ -12,11 +12,12 @@ mod tests { use crate::{ aggregates::site_aggregates::SiteAggregates, source::{ - comment::{Comment, CommentForm}, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, - post::{Post, PostForm}, - site::{Site, SiteForm}, + comment::{Comment, CommentInsertForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + post::{Post, PostInsertForm}, + site::{Site, SiteInsertForm}, }, traits::Crud, utils::establish_unpooled_connection, @@ -28,58 +29,56 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy_site_agg".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy_site_agg".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let site_form = SiteForm { - name: "test_site".into(), - public_key: Some("pubkey".to_string()), - ..Default::default() - }; + let site_form = SiteInsertForm::builder() + .name("test_site".into()) + .instance_id(inserted_instance.id) + .build(); let inserted_site = Site::create(conn, &site_form).unwrap(); - let new_community = CommunityForm { - name: "TIL_site_agg".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL_site_agg".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); // Insert two of those posts let inserted_post = Post::create(conn, &new_post).unwrap(); let _inserted_post_again = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); // Insert two of those comments let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let child_comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let _inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); @@ -113,5 +112,7 @@ mod tests { Site::delete(conn, inserted_site.id).unwrap(); let after_delete_site = SiteAggregates::read(conn); assert!(after_delete_site.is_err()); + + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_schema/src/impls/activity.rs b/crates/db_schema/src/impls/activity.rs index 87b78a1d..c24a66bd 100644 --- a/crates/db_schema/src/impls/activity.rs +++ b/crates/db_schema/src/impls/activity.rs @@ -7,14 +7,15 @@ use diesel::{ use serde_json::Value; impl Crud for Activity { - type Form = ActivityForm; + type InsertForm = ActivityInsertForm; + type UpdateForm = ActivityUpdateForm; type IdType = i32; fn read(conn: &mut PgConnection, activity_id: i32) -> Result { use crate::schema::activity::dsl::*; activity.find(activity_id).first::(conn) } - fn create(conn: &mut PgConnection, new_activity: &ActivityForm) -> Result { + fn create(conn: &mut PgConnection, new_activity: &Self::InsertForm) -> Result { use crate::schema::activity::dsl::*; insert_into(activity) .values(new_activity) @@ -24,7 +25,7 @@ impl Crud for Activity { fn update( conn: &mut PgConnection, activity_id: i32, - new_activity: &ActivityForm, + new_activity: &Self::UpdateForm, ) -> Result { use crate::schema::activity::dsl::*; diesel::update(activity.find(activity_id)) @@ -39,14 +40,15 @@ impl Crud for Activity { impl Activity { /// Returns true if the insert was successful + // TODO this should probably just be changed to an upsert on_conflict, rather than an error pub fn insert( conn: &mut PgConnection, ap_id: DbUrl, data: Value, local: bool, - sensitive: bool, + sensitive: Option, ) -> Result { - let activity_form = ActivityForm { + let activity_form = ActivityInsertForm { ap_id, data, local: Some(local), @@ -81,8 +83,9 @@ mod tests { use crate::{ newtypes::DbUrl, source::{ - activity::{Activity, ActivityForm}, - person::{Person, PersonForm}, + activity::{Activity, ActivityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, }, utils::establish_unpooled_connection, }; @@ -95,11 +98,13 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let creator_form = PersonForm { - name: "activity_creator_pm".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let creator_form = PersonInsertForm::builder() + .name("activity_creator_ pm".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_creator = Person::create(conn, &creator_form).unwrap(); @@ -122,11 +127,11 @@ mod tests { }"#, ) .unwrap(); - let activity_form = ActivityForm { + let activity_form = ActivityInsertForm { ap_id: ap_id.clone(), data: test_json.to_owned(), local: Some(true), - sensitive: false, + sensitive: Some(false), updated: None, }; diff --git a/crates/db_schema/src/impls/actor_language.rs b/crates/db_schema/src/impls/actor_language.rs index c5fa7f32..1de5c6aa 100644 --- a/crates/db_schema/src/impls/actor_language.rs +++ b/crates/db_schema/src/impls/actor_language.rs @@ -61,16 +61,11 @@ impl LocalUserLanguage { impl SiteLanguage { pub fn read_local(conn: &mut PgConnection) -> Result, Error> { - use crate::schema::{site, site_language::dsl::*}; - // TODO: remove this subquery once site.local column is added - let subquery = crate::schema::site::dsl::site - .order_by(site::id) - .select(site::id) - .limit(1) - .into_boxed(); - site_language - .filter(site_id.eq_any(subquery)) - .select(language_id) + use crate::schema::{local_site, site, site_language}; + site::table + .inner_join(local_site::table) + .inner_join(site_language::table) + .select(site_language::language_id) .load(conn) } @@ -264,10 +259,12 @@ mod tests { use crate::{ impls::actor_language::*, source::{ - community::{Community, CommunityForm}, - local_user::{LocalUser, LocalUserForm}, - person::{Person, PersonForm}, - site::{Site, SiteForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + local_site::{LocalSite, LocalSiteInsertForm}, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm}, + site::{Site, SiteInsertForm}, }, traits::Crud, utils::establish_unpooled_connection, @@ -288,12 +285,20 @@ mod tests { ] } - fn create_test_site(conn: &mut PgConnection) -> Site { - let site_form = SiteForm { - name: "test site".to_string(), - ..Default::default() - }; - Site::create(conn, &site_form).unwrap() + fn create_test_site(conn: &mut PgConnection) -> (Site, Instance) { + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let site_form = SiteInsertForm::builder() + .name("test site".to_string()) + .instance_id(inserted_instance.id) + .build(); + let site = Site::create(conn, &site_form).unwrap(); + + // Create a local site, since this is necessary for local languages + let local_site_form = LocalSiteInsertForm::builder().site_id(site.id).build(); + LocalSite::create(conn, &local_site_form).unwrap(); + + (site, inserted_instance) } #[test] @@ -332,7 +337,7 @@ mod tests { fn test_site_languages() { let conn = &mut establish_unpooled_connection(); - let site = create_test_site(conn); + let (site, instance) = create_test_site(conn); let site_languages1 = SiteLanguage::read_local(conn).unwrap(); // site is created with all languages assert_eq!(184, site_languages1.len()); @@ -345,6 +350,8 @@ mod tests { assert_eq!(test_langs, site_languages2); Site::delete(conn, site.id).unwrap(); + Instance::delete(conn, instance.id).unwrap(); + LocalSite::delete(conn).unwrap(); } #[test] @@ -352,21 +359,21 @@ mod tests { fn test_user_languages() { let conn = &mut establish_unpooled_connection(); - let site = create_test_site(conn); + let (site, instance) = create_test_site(conn); let test_langs = test_langs1(conn); SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap(); - let person_form = PersonForm { - name: "my test person".to_string(), - public_key: Some("pubkey".to_string()), - ..Default::default() - }; + let person_form = PersonInsertForm::builder() + .name("my test person".to_string()) + .public_key("pubkey".to_string()) + .instance_id(instance.id) + .build(); let person = Person::create(conn, &person_form).unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(person.id), - password_encrypted: Some("my_pw".to_string()), - ..Default::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(person.id) + .password_encrypted("my_pw".to_string()) + .build(); + let local_user = LocalUser::create(conn, &local_user_form).unwrap(); let local_user_langs1 = LocalUserLanguage::read(conn, local_user.id).unwrap(); @@ -382,24 +389,34 @@ mod tests { Person::delete(conn, person.id).unwrap(); LocalUser::delete(conn, local_user.id).unwrap(); Site::delete(conn, site.id).unwrap(); + LocalSite::delete(conn).unwrap(); + Instance::delete(conn, instance.id).unwrap(); } #[test] #[serial] fn test_community_languages() { let conn = &mut establish_unpooled_connection(); - let site = create_test_site(conn); + let (site, instance) = create_test_site(conn); let test_langs = test_langs1(conn); SiteLanguage::update(conn, test_langs.clone(), site.id).unwrap(); - let community_form = CommunityForm { - name: "test community".to_string(), - title: "test community".to_string(), - public_key: Some("pubkey".to_string()), - ..Default::default() - }; + let read_site_langs = SiteLanguage::read(conn, site.id).unwrap(); + assert_eq!(test_langs, read_site_langs); + + // Test the local ones are the same + let read_local_site_langs = SiteLanguage::read_local(conn).unwrap(); + assert_eq!(test_langs, read_local_site_langs); + + let community_form = CommunityInsertForm::builder() + .name("test community".to_string()) + .title("test community".to_string()) + .public_key("pubkey".to_string()) + .instance_id(instance.id) + .build(); let community = Community::create(conn, &community_form).unwrap(); let community_langs1 = CommunityLanguage::read(conn, community.id).unwrap(); + // community is initialized with site languages assert_eq!(test_langs, community_langs1); @@ -423,37 +440,39 @@ mod tests { let community_langs3 = CommunityLanguage::read(conn, community.id).unwrap(); assert_eq!(test_langs2, community_langs3); - Site::delete(conn, site.id).unwrap(); Community::delete(conn, community.id).unwrap(); + Site::delete(conn, site.id).unwrap(); + LocalSite::delete(conn).unwrap(); + Instance::delete(conn, instance.id).unwrap(); } #[test] #[serial] fn test_default_post_language() { let conn = &mut establish_unpooled_connection(); + let (site, instance) = create_test_site(conn); let test_langs = test_langs1(conn); let test_langs2 = test_langs2(conn); - let community_form = CommunityForm { - name: "test community".to_string(), - title: "test community".to_string(), - public_key: Some("pubkey".to_string()), - ..Default::default() - }; + let community_form = CommunityInsertForm::builder() + .name("test community".to_string()) + .title("test community".to_string()) + .public_key("pubkey".to_string()) + .instance_id(instance.id) + .build(); let community = Community::create(conn, &community_form).unwrap(); CommunityLanguage::update(conn, test_langs, community.id).unwrap(); - let person_form = PersonForm { - name: "my test person".to_string(), - public_key: Some("pubkey".to_string()), - ..Default::default() - }; + let person_form = PersonInsertForm::builder() + .name("my test person".to_string()) + .public_key("pubkey".to_string()) + .instance_id(instance.id) + .build(); let person = Person::create(conn, &person_form).unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(person.id), - password_encrypted: Some("my_pw".to_string()), - ..Default::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(person.id) + .password_encrypted("my_pw".to_string()) + .build(); let local_user = LocalUser::create(conn, &local_user_form).unwrap(); LocalUserLanguage::update(conn, test_langs2, local_user.id).unwrap(); @@ -476,5 +495,8 @@ mod tests { Person::delete(conn, person.id).unwrap(); Community::delete(conn, community.id).unwrap(); LocalUser::delete(conn, local_user.id).unwrap(); + Site::delete(conn, site.id).unwrap(); + LocalSite::delete(conn).unwrap(); + Instance::delete(conn, instance.id).unwrap(); } } diff --git a/crates/db_schema/src/impls/comment.rs b/crates/db_schema/src/impls/comment.rs index eca5328d..d056f1a2 100644 --- a/crates/db_schema/src/impls/comment.rs +++ b/crates/db_schema/src/impls/comment.rs @@ -2,11 +2,12 @@ use crate::{ newtypes::{CommentId, DbUrl, PersonId}, source::comment::{ Comment, - CommentForm, + CommentInsertForm, CommentLike, CommentLikeForm, CommentSaved, CommentSavedForm, + CommentUpdateForm, }, traits::{Crud, DeleteableOrRemoveable, Likeable, Saveable}, utils::naive_now, @@ -16,18 +17,6 @@ use diesel_ltree::Ltree; use url::Url; impl Comment { - pub fn update_ap_id( - conn: &mut PgConnection, - comment_id: CommentId, - apub_id: DbUrl, - ) -> Result { - use crate::schema::comment::dsl::*; - - diesel::update(comment.find(comment_id)) - .set(ap_id.eq(apub_id)) - .get_result::(conn) - } - pub fn permadelete_for_creator( conn: &mut PgConnection, for_creator_id: PersonId, @@ -42,28 +31,6 @@ impl Comment { .get_results::(conn) } - pub fn update_deleted( - conn: &mut PgConnection, - comment_id: CommentId, - new_deleted: bool, - ) -> Result { - use crate::schema::comment::dsl::*; - diesel::update(comment.find(comment_id)) - .set((deleted.eq(new_deleted), updated.eq(naive_now()))) - .get_result::(conn) - } - - pub fn update_removed( - conn: &mut PgConnection, - comment_id: CommentId, - new_removed: bool, - ) -> Result { - use crate::schema::comment::dsl::*; - diesel::update(comment.find(comment_id)) - .set((removed.eq(new_removed), updated.eq(naive_now()))) - .get_result::(conn) - } - pub fn update_removed_for_creator( conn: &mut PgConnection, for_creator_id: PersonId, @@ -77,7 +44,7 @@ impl Comment { pub fn create( conn: &mut PgConnection, - comment_form: &CommentForm, + comment_form: &CommentInsertForm, parent_path: Option<&Ltree>, ) -> Result { use crate::schema::comment::dsl::*; @@ -168,7 +135,8 @@ where ca.comment_id = c.id", } impl Crud for Comment { - type Form = CommentForm; + type InsertForm = CommentInsertForm; + type UpdateForm = CommentUpdateForm; type IdType = CommentId; fn read(conn: &mut PgConnection, comment_id: CommentId) -> Result { use crate::schema::comment::dsl::*; @@ -181,14 +149,14 @@ impl Crud for Comment { } /// This is unimplemented, use [[Comment::create]] - fn create(_conn: &mut PgConnection, _comment_form: &CommentForm) -> Result { + fn create(_conn: &mut PgConnection, _comment_form: &Self::InsertForm) -> Result { unimplemented!(); } fn update( conn: &mut PgConnection, comment_id: CommentId, - comment_form: &CommentForm, + comment_form: &Self::UpdateForm, ) -> Result { use crate::schema::comment::dsl::*; diesel::update(comment.find(comment_id)) @@ -262,8 +230,9 @@ mod tests { newtypes::LanguageId, source::{ comment::*, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, + person::{Person, PersonInsertForm}, post::*, }, traits::{Crud, Likeable, Saveable}, @@ -277,38 +246,38 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "terry".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("terry".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let new_community = CommunityForm { - name: "test community".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); @@ -328,13 +297,11 @@ mod tests { language_id: LanguageId::default(), }; - let child_comment_form = CommentForm { - content: "A child comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - // path: Some(text2ltree(inserted_comment.id), - ..CommentForm::default() - }; + let child_comment_form = CommentInsertForm::builder() + .content("A child comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_child_comment = Comment::create(conn, &child_comment_form, Some(&inserted_comment.path)).unwrap(); @@ -373,8 +340,13 @@ mod tests { published: inserted_comment_saved.published, }; + let comment_update_form = CommentUpdateForm::builder() + .content(Some("A test comment".into())) + .build(); + + let updated_comment = Comment::update(conn, inserted_comment.id, &comment_update_form).unwrap(); + let read_comment = Comment::read(conn, inserted_comment.id).unwrap(); - let updated_comment = Comment::update(conn, inserted_comment.id, &comment_form).unwrap(); let like_removed = CommentLike::remove(conn, inserted_person.id, inserted_comment.id).unwrap(); let saved_removed = CommentSaved::unsave(conn, &comment_saved_form).unwrap(); let num_deleted = Comment::delete(conn, inserted_comment.id).unwrap(); @@ -382,6 +354,7 @@ mod tests { Post::delete(conn, inserted_post.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_comment, read_comment); assert_eq!(expected_comment, inserted_comment); diff --git a/crates/db_schema/src/impls/comment_reply.rs b/crates/db_schema/src/impls/comment_reply.rs index 8fed2ce4..84d6fda5 100644 --- a/crates/db_schema/src/impls/comment_reply.rs +++ b/crates/db_schema/src/impls/comment_reply.rs @@ -6,14 +6,15 @@ use crate::{ use diesel::{dsl::*, result::Error, *}; impl Crud for CommentReply { - type Form = CommentReplyForm; + type InsertForm = CommentReplyInsertForm; + type UpdateForm = CommentReplyUpdateForm; type IdType = CommentReplyId; fn read(conn: &mut PgConnection, comment_reply_id: CommentReplyId) -> Result { use crate::schema::comment_reply::dsl::*; comment_reply.find(comment_reply_id).first::(conn) } - fn create(conn: &mut PgConnection, comment_reply_form: &CommentReplyForm) -> Result { + fn create(conn: &mut PgConnection, comment_reply_form: &Self::InsertForm) -> Result { use crate::schema::comment_reply::dsl::*; // since the return here isnt utilized, we dont need to do an update // but get_result doesnt return the existing row here @@ -28,7 +29,7 @@ impl Crud for CommentReply { fn update( conn: &mut PgConnection, comment_reply_id: CommentReplyId, - comment_reply_form: &CommentReplyForm, + comment_reply_form: &Self::UpdateForm, ) -> Result { use crate::schema::comment_reply::dsl::*; diesel::update(comment_reply.find(comment_reply_id)) @@ -38,17 +39,6 @@ impl Crud for CommentReply { } impl CommentReply { - pub fn update_read( - conn: &mut PgConnection, - comment_reply_id: CommentReplyId, - new_read: bool, - ) -> Result { - use crate::schema::comment_reply::dsl::*; - diesel::update(comment_reply.find(comment_reply_id)) - .set(read.eq(new_read)) - .get_result::(conn) - } - pub fn mark_all_as_read( conn: &mut PgConnection, for_recipient_id: PersonId, @@ -80,7 +70,8 @@ mod tests { source::{ comment::*, comment_reply::*, - community::{Community, CommunityForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, person::*, post::*, }, @@ -94,50 +85,50 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "terrylake".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("terrylake".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let recipient_form = PersonForm { - name: "terrylakes recipient".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let recipient_form = PersonInsertForm::builder() + .name("terrylakes recipient".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_recipient = Person::create(conn, &recipient_form).unwrap(); - let new_community = CommunityForm { - name: "test community lake".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community lake".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let comment_reply_form = CommentReplyForm { + let comment_reply_form = CommentReplyInsertForm { recipient_id: inserted_recipient.id, comment_id: inserted_comment.id, read: None, @@ -154,12 +145,17 @@ mod tests { }; let read_reply = CommentReply::read(conn, inserted_reply.id).unwrap(); - let updated_reply = CommentReply::update(conn, inserted_reply.id, &comment_reply_form).unwrap(); + + let comment_reply_update_form = CommentReplyUpdateForm { read: Some(false) }; + let updated_reply = + CommentReply::update(conn, inserted_reply.id, &comment_reply_update_form).unwrap(); + Comment::delete(conn, inserted_comment.id).unwrap(); Post::delete(conn, inserted_post.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); Person::delete(conn, inserted_recipient.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_reply, read_reply); assert_eq!(expected_reply, inserted_reply); diff --git a/crates/db_schema/src/impls/community.rs b/crates/db_schema/src/impls/community.rs index 966761b2..47c7b0e0 100644 --- a/crates/db_schema/src/impls/community.rs +++ b/crates/db_schema/src/impls/community.rs @@ -6,16 +6,17 @@ use crate::{ Community, CommunityFollower, CommunityFollowerForm, - CommunityForm, + CommunityInsertForm, CommunityModerator, CommunityModeratorForm, CommunityPersonBan, CommunityPersonBanForm, CommunitySafe, + CommunityUpdateForm, }, }, traits::{ApubActor, Bannable, Crud, DeleteableOrRemoveable, Followable, Joinable}, - utils::{functions::lower, naive_now}, + utils::functions::lower, SubscribedType, }; use diesel::{ @@ -47,6 +48,7 @@ mod safe_type { banner, hidden, posting_restricted_to_mods, + instance_id, ); impl ToSafe for Community { @@ -68,13 +70,15 @@ mod safe_type { banner, hidden, posting_restricted_to_mods, + instance_id, ) } } } impl Crud for Community { - type Form = CommunityForm; + type InsertForm = CommunityInsertForm; + type UpdateForm = CommunityUpdateForm; type IdType = CommunityId; fn read(conn: &mut PgConnection, community_id: CommunityId) -> Result { use crate::schema::community::dsl::*; @@ -86,10 +90,13 @@ impl Crud for Community { diesel::delete(community.find(community_id)).execute(conn) } - fn create(conn: &mut PgConnection, new_community: &CommunityForm) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::community::dsl::*; let community_ = insert_into(community) - .values(new_community) + .values(form) + .on_conflict(actor_id) + .do_update() + .set(form) .get_result::(conn)?; let site_languages = SiteLanguage::read_local(conn); @@ -107,66 +114,11 @@ impl Crud for Community { fn update( conn: &mut PgConnection, community_id: CommunityId, - new_community: &CommunityForm, + form: &Self::UpdateForm, ) -> Result { use crate::schema::community::dsl::*; diesel::update(community.find(community_id)) - .set(new_community) - .get_result::(conn) - } -} - -impl Community { - pub fn update_deleted( - conn: &mut PgConnection, - community_id: CommunityId, - new_deleted: bool, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set((deleted.eq(new_deleted), updated.eq(naive_now()))) - .get_result::(conn) - } - - pub fn update_removed( - conn: &mut PgConnection, - community_id: CommunityId, - new_removed: bool, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set((removed.eq(new_removed), updated.eq(naive_now()))) - .get_result::(conn) - } - - pub fn distinct_federated_communities(conn: &mut PgConnection) -> Result, Error> { - use crate::schema::community::dsl::*; - community.select(actor_id).distinct().load::(conn) - } - - pub fn upsert( - conn: &mut PgConnection, - community_form: &CommunityForm, - ) -> Result { - use crate::schema::community::dsl::*; - insert_into(community) - .values(community_form) - .on_conflict(actor_id) - .do_update() - .set(community_form) - .get_result::(conn) - } - - pub fn remove_avatar_and_banner( - conn: &mut PgConnection, - community_id: CommunityId, - ) -> Result { - use crate::schema::community::dsl::*; - diesel::update(community.find(community_id)) - .set(( - icon.eq::>(None), - banner.eq::>(None), - )) + .set(form) .get_result::(conn) } } @@ -384,7 +336,7 @@ impl ApubActor for Community { #[cfg(test)] mod tests { use crate::{ - source::{community::*, person::*}, + source::{community::*, instance::Instance, person::*}, traits::{Bannable, Crud, Followable, Joinable}, utils::establish_unpooled_connection, }; @@ -395,20 +347,22 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "bobbee".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("bobbee".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let new_community = CommunityForm { - name: "TIL".into(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("TIL".into()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); @@ -434,6 +388,7 @@ mod tests { shared_inbox_url: None, hidden: false, posting_restricted_to_mods: false, + instance_id: inserted_instance.id, }; let community_follower_form = CommunityFollowerForm { @@ -486,12 +441,19 @@ mod tests { }; let read_community = Community::read(conn, inserted_community.id).unwrap(); - let updated_community = Community::update(conn, inserted_community.id, &new_community).unwrap(); + + let update_community_form = CommunityUpdateForm::builder() + .title(Some("nada".to_owned())) + .build(); + let updated_community = + Community::update(conn, inserted_community.id, &update_community_form).unwrap(); + let ignored_community = CommunityFollower::unfollow(conn, &community_follower_form).unwrap(); let left_community = CommunityModerator::leave(conn, &community_moderator_form).unwrap(); let unban = CommunityPersonBan::unban(conn, &community_person_ban_form).unwrap(); let num_deleted = Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_community, read_community); assert_eq!(expected_community, inserted_community); diff --git a/crates/db_schema/src/impls/email_verification.rs b/crates/db_schema/src/impls/email_verification.rs index 33415cf0..c6140f4b 100644 --- a/crates/db_schema/src/impls/email_verification.rs +++ b/crates/db_schema/src/impls/email_verification.rs @@ -1,4 +1,4 @@ -use crate::{newtypes::LocalUserId, source::email_verification::*, traits::Crud}; +use crate::{newtypes::LocalUserId, source::email_verification::*}; use diesel::{ dsl::*, insert_into, @@ -9,39 +9,14 @@ use diesel::{ RunQueryDsl, }; -impl Crud for EmailVerification { - type Form = EmailVerificationForm; - type IdType = i32; - fn create(conn: &mut PgConnection, form: &EmailVerificationForm) -> Result { +impl EmailVerification { + pub fn create(conn: &mut PgConnection, form: &EmailVerificationForm) -> Result { use crate::schema::email_verification::dsl::*; insert_into(email_verification) .values(form) .get_result::(conn) } - fn read(conn: &mut PgConnection, id_: i32) -> Result { - use crate::schema::email_verification::dsl::*; - email_verification.find(id_).first::(conn) - } - - fn update( - conn: &mut PgConnection, - id_: i32, - form: &EmailVerificationForm, - ) -> Result { - use crate::schema::email_verification::dsl::*; - diesel::update(email_verification.find(id_)) - .set(form) - .get_result::(conn) - } - - fn delete(conn: &mut PgConnection, id_: i32) -> Result { - use crate::schema::email_verification::dsl::*; - diesel::delete(email_verification.find(id_)).execute(conn) - } -} - -impl EmailVerification { pub fn read_for_token(conn: &mut PgConnection, token: &str) -> Result { use crate::schema::email_verification::dsl::*; email_verification diff --git a/crates/db_schema/src/impls/federation_allowlist.rs b/crates/db_schema/src/impls/federation_allowlist.rs new file mode 100644 index 00000000..406b0e2c --- /dev/null +++ b/crates/db_schema/src/impls/federation_allowlist.rs @@ -0,0 +1,81 @@ +use crate::{ + schema::federation_allowlist, + source::{ + federation_allowlist::{FederationAllowList, FederationAllowListForm}, + instance::Instance, + }, +}; +use diesel::{dsl::*, result::Error, *}; + +impl FederationAllowList { + pub fn replace(conn: &mut PgConnection, list_opt: Option>) -> Result<(), Error> { + conn.build_transaction().read_write().run(|conn| { + if let Some(list) = list_opt { + Self::clear(conn)?; + + for domain in list { + // Upsert all of these as instances + let instance = Instance::create(conn, &domain)?; + + let form = FederationAllowListForm { + instance_id: instance.id, + updated: None, + }; + insert_into(federation_allowlist::table) + .values(form) + .get_result::(conn)?; + } + Ok(()) + } else { + Ok(()) + } + }) + } + + pub fn clear(conn: &mut PgConnection) -> Result { + diesel::delete(federation_allowlist::table).execute(conn) + } +} +#[cfg(test)] +mod tests { + use crate::{ + source::{federation_allowlist::FederationAllowList, instance::Instance}, + utils::establish_unpooled_connection, + }; + use serial_test::serial; + + #[test] + #[serial] + fn test_allowlist_insert_and_clear() { + let conn = &mut establish_unpooled_connection(); + let allowed = Some(vec![ + "tld1.xyz".to_string(), + "tld2.xyz".to_string(), + "tld3.xyz".to_string(), + ]); + + FederationAllowList::replace(conn, allowed).unwrap(); + + let allows = Instance::allowlist(conn).unwrap(); + + assert_eq!(3, allows.len()); + assert_eq!( + vec![ + "tld1.xyz".to_string(), + "tld2.xyz".to_string(), + "tld3.xyz".to_string() + ], + allows + ); + + // Now test clearing them via Some(empty vec) + let clear_allows = Some(Vec::new()); + + FederationAllowList::replace(conn, clear_allows).unwrap(); + let allows = Instance::allowlist(conn).unwrap(); + + assert_eq!(0, allows.len()); + + Instance::delete_all(conn).unwrap(); + } +} diff --git a/crates/db_schema/src/impls/federation_blocklist.rs b/crates/db_schema/src/impls/federation_blocklist.rs new file mode 100644 index 00000000..58ea4e91 --- /dev/null +++ b/crates/db_schema/src/impls/federation_blocklist.rs @@ -0,0 +1,38 @@ +use crate::{ + schema::federation_blocklist, + source::{ + federation_blocklist::{FederationBlockList, FederationBlockListForm}, + instance::Instance, + }, +}; +use diesel::{dsl::*, result::Error, *}; + +impl FederationBlockList { + pub fn replace(conn: &mut PgConnection, list_opt: Option>) -> Result<(), Error> { + conn.build_transaction().read_write().run(|conn| { + if let Some(list) = list_opt { + Self::clear(conn)?; + + for domain in list { + // Upsert all of these as instances + let instance = Instance::create(conn, &domain)?; + + let form = FederationBlockListForm { + instance_id: instance.id, + updated: None, + }; + insert_into(federation_blocklist::table) + .values(form) + .get_result::(conn)?; + } + Ok(()) + } else { + Ok(()) + } + }) + } + + pub fn clear(conn: &mut PgConnection) -> Result { + diesel::delete(federation_blocklist::table).execute(conn) + } +} diff --git a/crates/db_schema/src/impls/instance.rs b/crates/db_schema/src/impls/instance.rs new file mode 100644 index 00000000..c2afa6ae --- /dev/null +++ b/crates/db_schema/src/impls/instance.rs @@ -0,0 +1,59 @@ +use crate::{ + newtypes::InstanceId, + schema::{federation_allowlist, federation_blocklist, instance}, + source::instance::{Instance, InstanceForm}, + utils::naive_now, +}; +use diesel::{dsl::*, result::Error, *}; +use lemmy_utils::utils::generate_domain_url; +use url::Url; + +impl Instance { + fn create_from_form(conn: &mut PgConnection, form: &InstanceForm) -> Result { + // Do upsert on domain name conflict + insert_into(instance::table) + .values(form) + .on_conflict(instance::domain) + .do_update() + .set(form) + .get_result::(conn) + } + pub fn create(conn: &mut PgConnection, domain: &str) -> Result { + let form = InstanceForm { + domain: domain.to_string(), + updated: Some(naive_now()), + }; + Self::create_from_form(conn, &form) + } + pub fn create_from_actor_id(conn: &mut PgConnection, actor_id: &Url) -> Result { + let domain = &generate_domain_url(actor_id).expect("actor id missing a domain"); + Self::create(conn, domain) + } + pub fn delete(conn: &mut PgConnection, instance_id: InstanceId) -> Result { + diesel::delete(instance::table.find(instance_id)).execute(conn) + } + pub fn delete_all(conn: &mut PgConnection) -> Result { + diesel::delete(instance::table).execute(conn) + } + pub fn allowlist(conn: &mut PgConnection) -> Result, Error> { + instance::table + .inner_join(federation_allowlist::table) + .select(instance::domain) + .load::(conn) + } + + pub fn blocklist(conn: &mut PgConnection) -> Result, Error> { + instance::table + .inner_join(federation_blocklist::table) + .select(instance::domain) + .load::(conn) + } + + pub fn linked(conn: &mut PgConnection) -> Result, Error> { + instance::table + .left_join(federation_blocklist::table) + .filter(federation_blocklist::id.is_null()) + .select(instance::domain) + .load::(conn) + } +} diff --git a/crates/db_schema/src/impls/local_site.rs b/crates/db_schema/src/impls/local_site.rs new file mode 100644 index 00000000..4686ac80 --- /dev/null +++ b/crates/db_schema/src/impls/local_site.rs @@ -0,0 +1,21 @@ +use crate::{schema::local_site::dsl::*, source::local_site::*}; +use diesel::{dsl::*, result::Error, *}; + +impl LocalSite { + pub fn create(conn: &mut PgConnection, form: &LocalSiteInsertForm) -> Result { + insert_into(local_site) + .values(form) + .get_result::(conn) + } + pub fn read(conn: &mut PgConnection) -> Result { + local_site.first::(conn) + } + pub fn update(conn: &mut PgConnection, form: &LocalSiteUpdateForm) -> Result { + diesel::update(local_site) + .set(form) + .get_result::(conn) + } + pub fn delete(conn: &mut PgConnection) -> Result { + diesel::delete(local_site).execute(conn) + } +} diff --git a/crates/db_schema/src/impls/local_site_rate_limit.rs b/crates/db_schema/src/impls/local_site_rate_limit.rs new file mode 100644 index 00000000..55ab3984 --- /dev/null +++ b/crates/db_schema/src/impls/local_site_rate_limit.rs @@ -0,0 +1,25 @@ +use crate::{schema::local_site_rate_limit, source::local_site_rate_limit::*}; +use diesel::{dsl::*, result::Error, *}; + +impl LocalSiteRateLimit { + pub fn read(conn: &mut PgConnection) -> Result { + local_site_rate_limit::table.first::(conn) + } + + pub fn create( + conn: &mut PgConnection, + form: &LocalSiteRateLimitInsertForm, + ) -> Result { + insert_into(local_site_rate_limit::table) + .values(form) + .get_result::(conn) + } + pub fn update( + conn: &mut PgConnection, + form: &LocalSiteRateLimitUpdateForm, + ) -> Result { + diesel::update(local_site_rate_limit::table) + .set(form) + .get_result::(conn) + } +} diff --git a/crates/db_schema/src/impls/local_user.rs b/crates/db_schema/src/impls/local_user.rs index 31eded1a..9f2c1c3c 100644 --- a/crates/db_schema/src/impls/local_user.rs +++ b/crates/db_schema/src/impls/local_user.rs @@ -3,7 +3,7 @@ use crate::{ schema::local_user::dsl::*, source::{ actor_language::{LocalUserLanguage, SiteLanguage}, - local_user::{LocalUser, LocalUserForm}, + local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, }, traits::Crud, utils::naive_now, @@ -67,17 +67,6 @@ mod safe_settings_type { } impl LocalUser { - pub fn register(conn: &mut PgConnection, form: &LocalUserForm) -> Result { - let mut edited_user = form.clone(); - let password_hash = form - .password_encrypted - .as_ref() - .map(|p| hash(p, DEFAULT_COST).expect("Couldn't hash password")); - edited_user.password_encrypted = password_hash; - - Self::create(conn, &edited_user) - } - pub fn update_password( conn: &mut PgConnection, local_user_id: LocalUserId, @@ -109,7 +98,8 @@ impl LocalUser { } impl Crud for LocalUser { - type Form = LocalUserForm; + type InsertForm = LocalUserInsertForm; + type UpdateForm = LocalUserUpdateForm; type IdType = LocalUserId; fn read(conn: &mut PgConnection, local_user_id: LocalUserId) -> Result { local_user.find(local_user_id).first::(conn) @@ -117,9 +107,14 @@ impl Crud for LocalUser { fn delete(conn: &mut PgConnection, local_user_id: LocalUserId) -> Result { diesel::delete(local_user.find(local_user_id)).execute(conn) } - fn create(conn: &mut PgConnection, form: &LocalUserForm) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { + let mut form_with_encrypted_password = form.clone(); + let password_hash = + hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password"); + form_with_encrypted_password.password_encrypted = password_hash; + let local_user_ = insert_into(local_user) - .values(form) + .values(form_with_encrypted_password) .get_result::(conn)?; let site_languages = SiteLanguage::read_local(conn); @@ -137,7 +132,7 @@ impl Crud for LocalUser { fn update( conn: &mut PgConnection, local_user_id: LocalUserId, - form: &LocalUserForm, + form: &Self::UpdateForm, ) -> Result { diesel::update(local_user.find(local_user_id)) .set(form) diff --git a/crates/db_schema/src/impls/mod.rs b/crates/db_schema/src/impls/mod.rs index ba95a18f..b1570fe9 100644 --- a/crates/db_schema/src/impls/mod.rs +++ b/crates/db_schema/src/impls/mod.rs @@ -6,7 +6,12 @@ pub mod comment_report; pub mod community; pub mod community_block; pub mod email_verification; +pub mod federation_allowlist; +pub mod federation_blocklist; +pub mod instance; pub mod language; +pub mod local_site; +pub mod local_site_rate_limit; pub mod local_user; pub mod moderator; pub mod password_reset_request; diff --git a/crates/db_schema/src/impls/moderator.rs b/crates/db_schema/src/impls/moderator.rs index 6ec2f81b..824001de 100644 --- a/crates/db_schema/src/impls/moderator.rs +++ b/crates/db_schema/src/impls/moderator.rs @@ -1,8 +1,11 @@ use crate::{source::moderator::*, traits::Crud}; use diesel::{dsl::*, result::Error, *}; +// TODO grep for ..xxxDefault() + impl Crud for ModRemovePost { - type Form = ModRemovePostForm; + type InsertForm = ModRemovePostForm; + type UpdateForm = ModRemovePostForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_remove_post::dsl::*; @@ -29,7 +32,8 @@ impl Crud for ModRemovePost { } impl Crud for ModLockPost { - type Form = ModLockPostForm; + type InsertForm = ModLockPostForm; + type UpdateForm = ModLockPostForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_lock_post::dsl::*; @@ -52,7 +56,8 @@ impl Crud for ModLockPost { } impl Crud for ModStickyPost { - type Form = ModStickyPostForm; + type InsertForm = ModStickyPostForm; + type UpdateForm = ModStickyPostForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_sticky_post::dsl::*; @@ -79,7 +84,8 @@ impl Crud for ModStickyPost { } impl Crud for ModRemoveComment { - type Form = ModRemoveCommentForm; + type InsertForm = ModRemoveCommentForm; + type UpdateForm = ModRemoveCommentForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_remove_comment::dsl::*; @@ -106,7 +112,8 @@ impl Crud for ModRemoveComment { } impl Crud for ModRemoveCommunity { - type Form = ModRemoveCommunityForm; + type InsertForm = ModRemoveCommunityForm; + type UpdateForm = ModRemoveCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_remove_community::dsl::*; @@ -133,7 +140,8 @@ impl Crud for ModRemoveCommunity { } impl Crud for ModBanFromCommunity { - type Form = ModBanFromCommunityForm; + type InsertForm = ModBanFromCommunityForm; + type UpdateForm = ModBanFromCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_ban_from_community::dsl::*; @@ -160,7 +168,8 @@ impl Crud for ModBanFromCommunity { } impl Crud for ModBan { - type Form = ModBanForm; + type InsertForm = ModBanForm; + type UpdateForm = ModBanForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_ban::dsl::*; @@ -181,7 +190,8 @@ impl Crud for ModBan { } impl Crud for ModHideCommunity { - type Form = ModHideCommunityForm; + type InsertForm = ModHideCommunityForm; + type UpdateForm = ModHideCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { @@ -209,7 +219,8 @@ impl Crud for ModHideCommunity { } impl Crud for ModAddCommunity { - type Form = ModAddCommunityForm; + type InsertForm = ModAddCommunityForm; + type UpdateForm = ModAddCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_add_community::dsl::*; @@ -236,7 +247,8 @@ impl Crud for ModAddCommunity { } impl Crud for ModTransferCommunity { - type Form = ModTransferCommunityForm; + type InsertForm = ModTransferCommunityForm; + type UpdateForm = ModTransferCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_transfer_community::dsl::*; @@ -263,7 +275,8 @@ impl Crud for ModTransferCommunity { } impl Crud for ModAdd { - type Form = ModAddForm; + type InsertForm = ModAddForm; + type UpdateForm = ModAddForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::mod_add::dsl::*; @@ -284,21 +297,22 @@ impl Crud for ModAdd { } impl Crud for AdminPurgePerson { - type Form = AdminPurgePersonForm; + type InsertForm = AdminPurgePersonForm; + type UpdateForm = AdminPurgePersonForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::admin_purge_person::dsl::*; admin_purge_person.find(from_id).first::(conn) } - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_person::dsl::*; insert_into(admin_purge_person) .values(form) .get_result::(conn) } - fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result { + fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_person::dsl::*; diesel::update(admin_purge_person.find(from_id)) .set(form) @@ -307,21 +321,22 @@ impl Crud for AdminPurgePerson { } impl Crud for AdminPurgeCommunity { - type Form = AdminPurgeCommunityForm; + type InsertForm = AdminPurgeCommunityForm; + type UpdateForm = AdminPurgeCommunityForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::admin_purge_community::dsl::*; admin_purge_community.find(from_id).first::(conn) } - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_community::dsl::*; insert_into(admin_purge_community) .values(form) .get_result::(conn) } - fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result { + fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_community::dsl::*; diesel::update(admin_purge_community.find(from_id)) .set(form) @@ -330,21 +345,22 @@ impl Crud for AdminPurgeCommunity { } impl Crud for AdminPurgePost { - type Form = AdminPurgePostForm; + type InsertForm = AdminPurgePostForm; + type UpdateForm = AdminPurgePostForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::admin_purge_post::dsl::*; admin_purge_post.find(from_id).first::(conn) } - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_post::dsl::*; insert_into(admin_purge_post) .values(form) .get_result::(conn) } - fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result { + fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_post::dsl::*; diesel::update(admin_purge_post.find(from_id)) .set(form) @@ -353,21 +369,22 @@ impl Crud for AdminPurgePost { } impl Crud for AdminPurgeComment { - type Form = AdminPurgeCommentForm; + type InsertForm = AdminPurgeCommentForm; + type UpdateForm = AdminPurgeCommentForm; type IdType = i32; fn read(conn: &mut PgConnection, from_id: i32) -> Result { use crate::schema::admin_purge_comment::dsl::*; admin_purge_comment.find(from_id).first::(conn) } - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_comment::dsl::*; insert_into(admin_purge_comment) .values(form) .get_result::(conn) } - fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result { + fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result { use crate::schema::admin_purge_comment::dsl::*; diesel::update(admin_purge_comment.find(from_id)) .set(form) @@ -378,7 +395,7 @@ impl Crud for AdminPurgeComment { #[cfg(test)] mod tests { use crate::{ - source::{comment::*, community::*, moderator::*, person::*, post::*}, + source::{comment::*, community::*, instance::Instance, moderator::*, person::*, post::*}, traits::Crud, utils::establish_unpooled_connection, }; @@ -390,46 +407,46 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_mod = PersonForm { - name: "the mod".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_mod = PersonInsertForm::builder() + .name("the mod".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_mod = Person::create(conn, &new_mod).unwrap(); - let new_person = PersonForm { - name: "jim2".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person = PersonInsertForm::builder() + .name("jim2".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let new_community = CommunityForm { - name: "mod_community".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("mod_community".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post thweep".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post thweep".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); @@ -620,6 +637,7 @@ mod tests { Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); Person::delete(conn, inserted_mod.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_mod_remove_post, read_mod_remove_post); assert_eq!(expected_mod_lock_post, read_mod_lock_post); diff --git a/crates/db_schema/src/impls/password_reset_request.rs b/crates/db_schema/src/impls/password_reset_request.rs index dfa56e20..7a4d4c83 100644 --- a/crates/db_schema/src/impls/password_reset_request.rs +++ b/crates/db_schema/src/impls/password_reset_request.rs @@ -8,7 +8,8 @@ use diesel::{dsl::*, result::Error, PgConnection, *}; use sha2::{Digest, Sha256}; impl Crud for PasswordResetRequest { - type Form = PasswordResetRequestForm; + type InsertForm = PasswordResetRequestForm; + type UpdateForm = PasswordResetRequestForm; type IdType = i32; fn read(conn: &mut PgConnection, password_reset_request_id: i32) -> Result { password_reset_request @@ -74,7 +75,8 @@ fn bytes_to_hex(bytes: Vec) -> String { mod tests { use crate::{ source::{ - local_user::{LocalUser, LocalUserForm}, + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm}, password_reset_request::PasswordResetRequest, person::*, }, @@ -88,19 +90,20 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "thommy prw".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("thommy prw".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let new_local_user = LocalUserForm { - person_id: Some(inserted_person.id), - password_encrypted: Some("pass".to_string()), - ..LocalUserForm::default() - }; + let new_local_user = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .password_encrypted("pass".to_string()) + .build(); let inserted_local_user = LocalUser::create(conn, &new_local_user).unwrap(); @@ -119,6 +122,7 @@ mod tests { let read_password_reset_request = PasswordResetRequest::read_from_token(conn, token).unwrap(); let num_deleted = Person::delete(conn, inserted_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_password_reset_request, read_password_reset_request); assert_eq!( diff --git a/crates/db_schema/src/impls/person.rs b/crates/db_schema/src/impls/person.rs index 9c5dc5b6..22a26440 100644 --- a/crates/db_schema/src/impls/person.rs +++ b/crates/db_schema/src/impls/person.rs @@ -1,7 +1,7 @@ use crate::{ newtypes::{DbUrl, PersonId}, schema::person::dsl::*, - source::person::{Person, PersonForm}, + source::person::{Person, PersonInsertForm, PersonUpdateForm}, traits::{ApubActor, Crud}, utils::{functions::lower, naive_now}, }; @@ -37,6 +37,7 @@ mod safe_type { admin, bot_account, ban_expires, + instance_id, ); impl ToSafe for Person { @@ -61,13 +62,15 @@ mod safe_type { admin, bot_account, ban_expires, + instance_id, ) } } } impl Crud for Person { - type Form = PersonForm; + type InsertForm = PersonInsertForm; + type UpdateForm = PersonUpdateForm; type IdType = PersonId; fn read(conn: &mut PgConnection, person_id: PersonId) -> Result { person @@ -78,13 +81,18 @@ impl Crud for Person { fn delete(conn: &mut PgConnection, person_id: PersonId) -> Result { diesel::delete(person.find(person_id)).execute(conn) } - fn create(conn: &mut PgConnection, form: &PersonForm) -> Result { - insert_into(person).values(form).get_result::(conn) + fn create(conn: &mut PgConnection, form: &PersonInsertForm) -> Result { + insert_into(person) + .values(form) + .on_conflict(actor_id) + .do_update() + .set(form) + .get_result::(conn) } fn update( conn: &mut PgConnection, person_id: PersonId, - form: &PersonForm, + form: &PersonUpdateForm, ) -> Result { diesel::update(person.find(person_id)) .set(form) @@ -93,33 +101,6 @@ impl Crud for Person { } impl Person { - pub fn ban_person( - conn: &mut PgConnection, - person_id: PersonId, - ban: bool, - expires: Option, - ) -> Result { - diesel::update(person.find(person_id)) - .set((banned.eq(ban), ban_expires.eq(expires))) - .get_result::(conn) - } - - pub fn add_admin( - conn: &mut PgConnection, - person_id: PersonId, - added: bool, - ) -> Result { - diesel::update(person.find(person_id)) - .set(admin.eq(added)) - .get_result::(conn) - } - - pub fn mark_as_updated(conn: &mut PgConnection, person_id: PersonId) -> Result { - diesel::update(person.find(person_id)) - .set((last_refreshed_at.eq(naive_now()),)) - .get_result::(conn) - } - pub fn delete_account(conn: &mut PgConnection, person_id: PersonId) -> Result { use crate::schema::local_user; @@ -143,44 +124,6 @@ impl Person { )) .get_result::(conn) } - - pub fn upsert(conn: &mut PgConnection, person_form: &PersonForm) -> Result { - insert_into(person) - .values(person_form) - .on_conflict(actor_id) - .do_update() - .set(person_form) - .get_result::(conn) - } - - pub fn update_deleted( - conn: &mut PgConnection, - person_id: PersonId, - new_deleted: bool, - ) -> Result { - use crate::schema::person::dsl::*; - diesel::update(person.find(person_id)) - .set(deleted.eq(new_deleted)) - .get_result::(conn) - } - - pub fn leave_admin(conn: &mut PgConnection, person_id: PersonId) -> Result { - diesel::update(person.find(person_id)) - .set(admin.eq(false)) - .get_result::(conn) - } - - pub fn remove_avatar_and_banner( - conn: &mut PgConnection, - person_id: PersonId, - ) -> Result { - diesel::update(person.find(person_id)) - .set(( - avatar.eq::>(None), - banner.eq::>(None), - )) - .get_result::(conn) - } } pub fn is_banned(banned_: bool, expires: Option) -> bool { @@ -234,7 +177,11 @@ impl ApubActor for Person { #[cfg(test)] mod tests { - use crate::{source::person::*, traits::Crud, utils::establish_unpooled_connection}; + use crate::{ + source::{instance::Instance, person::*}, + traits::Crud, + utils::establish_unpooled_connection, + }; use serial_test::serial; #[test] @@ -242,11 +189,13 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "holly".into(), - public_key: Some("nada".to_owned()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("holly".into()) + .public_key("nada".to_owned()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); @@ -272,11 +221,18 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }; let read_person = Person::read(conn, inserted_person.id).unwrap(); - let updated_person = Person::update(conn, inserted_person.id, &new_person).unwrap(); + + let update_person_form = PersonUpdateForm::builder() + .actor_id(Some(inserted_person.actor_id.to_owned())) + .build(); + let updated_person = Person::update(conn, inserted_person.id, &update_person_form).unwrap(); + let num_deleted = Person::delete(conn, inserted_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_person, read_person); assert_eq!(expected_person, inserted_person); diff --git a/crates/db_schema/src/impls/person_mention.rs b/crates/db_schema/src/impls/person_mention.rs index fc91c76e..e83de1fe 100644 --- a/crates/db_schema/src/impls/person_mention.rs +++ b/crates/db_schema/src/impls/person_mention.rs @@ -6,7 +6,8 @@ use crate::{ use diesel::{dsl::*, result::Error, *}; impl Crud for PersonMention { - type Form = PersonMentionForm; + type InsertForm = PersonMentionInsertForm; + type UpdateForm = PersonMentionUpdateForm; type IdType = PersonMentionId; fn read(conn: &mut PgConnection, person_mention_id: PersonMentionId) -> Result { use crate::schema::person_mention::dsl::*; @@ -15,7 +16,7 @@ impl Crud for PersonMention { fn create( conn: &mut PgConnection, - person_mention_form: &PersonMentionForm, + person_mention_form: &Self::InsertForm, ) -> Result { use crate::schema::person_mention::dsl::*; // since the return here isnt utilized, we dont need to do an update @@ -31,7 +32,7 @@ impl Crud for PersonMention { fn update( conn: &mut PgConnection, person_mention_id: PersonMentionId, - person_mention_form: &PersonMentionForm, + person_mention_form: &Self::UpdateForm, ) -> Result { use crate::schema::person_mention::dsl::*; diesel::update(person_mention.find(person_mention_id)) @@ -41,17 +42,6 @@ impl Crud for PersonMention { } impl PersonMention { - pub fn update_read( - conn: &mut PgConnection, - person_mention_id: PersonMentionId, - new_read: bool, - ) -> Result { - use crate::schema::person_mention::dsl::*; - diesel::update(person_mention.find(person_mention_id)) - .set(read.eq(new_read)) - .get_result::(conn) - } - pub fn mark_all_as_read( conn: &mut PgConnection, for_recipient_id: PersonId, @@ -65,6 +55,7 @@ impl PersonMention { .set(read.eq(true)) .get_results::(conn) } + pub fn read_by_comment_and_person( conn: &mut PgConnection, for_comment_id: CommentId, @@ -83,7 +74,8 @@ mod tests { use crate::{ source::{ comment::*, - community::{Community, CommunityForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, person::*, person_mention::*, post::*, @@ -98,50 +90,50 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "terrylake".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("terrylake".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let recipient_form = PersonForm { - name: "terrylakes recipient".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let recipient_form = PersonInsertForm::builder() + .name("terrylakes recipient".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_recipient = Person::create(conn, &recipient_form).unwrap(); - let new_community = CommunityForm { - name: "test community lake".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community lake".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); - let person_mention_form = PersonMentionForm { + let person_mention_form = PersonMentionInsertForm { recipient_id: inserted_recipient.id, comment_id: inserted_comment.id, read: None, @@ -158,13 +150,16 @@ mod tests { }; let read_mention = PersonMention::read(conn, inserted_mention.id).unwrap(); + + let person_mention_update_form = PersonMentionUpdateForm { read: Some(false) }; let updated_mention = - PersonMention::update(conn, inserted_mention.id, &person_mention_form).unwrap(); + PersonMention::update(conn, inserted_mention.id, &person_mention_update_form).unwrap(); Comment::delete(conn, inserted_comment.id).unwrap(); Post::delete(conn, inserted_post.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); Person::delete(conn, inserted_recipient.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_mention, read_mention); assert_eq!(expected_mention, inserted_mention); diff --git a/crates/db_schema/src/impls/post.rs b/crates/db_schema/src/impls/post.rs index 4fa60159..95535b19 100644 --- a/crates/db_schema/src/impls/post.rs +++ b/crates/db_schema/src/impls/post.rs @@ -2,13 +2,14 @@ use crate::{ newtypes::{CommunityId, DbUrl, PersonId, PostId}, source::post::{ Post, - PostForm, + PostInsertForm, PostLike, PostLikeForm, PostRead, PostReadForm, PostSaved, PostSavedForm, + PostUpdateForm, }, traits::{Crud, DeleteableOrRemoveable, Likeable, Readable, Saveable}, utils::{naive_now, FETCH_LIMIT_MAX}, @@ -17,7 +18,8 @@ use diesel::{dsl::*, result::Error, ExpressionMethods, PgConnection, QueryDsl, R use url::Url; impl Crud for Post { - type Form = PostForm; + type InsertForm = PostInsertForm; + type UpdateForm = PostUpdateForm; type IdType = PostId; fn read(conn: &mut PgConnection, post_id: PostId) -> Result { use crate::schema::post::dsl::*; @@ -29,12 +31,21 @@ impl Crud for Post { diesel::delete(post.find(post_id)).execute(conn) } - fn create(conn: &mut PgConnection, new_post: &PostForm) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::post::dsl::*; - insert_into(post).values(new_post).get_result::(conn) + insert_into(post) + .values(form) + .on_conflict(ap_id) + .do_update() + .set(form) + .get_result::(conn) } - fn update(conn: &mut PgConnection, post_id: PostId, new_post: &PostForm) -> Result { + fn update( + conn: &mut PgConnection, + post_id: PostId, + new_post: &Self::UpdateForm, + ) -> Result { use crate::schema::post::dsl::*; diesel::update(post.find(post_id)) .set(new_post) @@ -58,18 +69,6 @@ impl Post { .load::(conn) } - pub fn update_ap_id( - conn: &mut PgConnection, - post_id: PostId, - apub_id: DbUrl, - ) -> Result { - use crate::schema::post::dsl::*; - - diesel::update(post.find(post_id)) - .set(ap_id.eq(apub_id)) - .get_result::(conn) - } - pub fn permadelete_for_creator( conn: &mut PgConnection, for_creator_id: PersonId, @@ -90,28 +89,6 @@ impl Post { .get_results::(conn) } - pub fn update_deleted( - conn: &mut PgConnection, - post_id: PostId, - new_deleted: bool, - ) -> Result { - use crate::schema::post::dsl::*; - diesel::update(post.find(post_id)) - .set((deleted.eq(new_deleted), updated.eq(naive_now()))) - .get_result::(conn) - } - - pub fn update_removed( - conn: &mut PgConnection, - post_id: PostId, - new_removed: bool, - ) -> Result { - use crate::schema::post::dsl::*; - diesel::update(post.find(post_id)) - .set((removed.eq(new_removed), updated.eq(naive_now()))) - .get_result::(conn) - } - pub fn update_removed_for_creator( conn: &mut PgConnection, for_creator_id: PersonId, @@ -132,41 +109,10 @@ impl Post { .get_results::(conn) } - pub fn update_locked( - conn: &mut PgConnection, - post_id: PostId, - new_locked: bool, - ) -> Result { - use crate::schema::post::dsl::*; - diesel::update(post.find(post_id)) - .set(locked.eq(new_locked)) - .get_result::(conn) - } - - pub fn update_stickied( - conn: &mut PgConnection, - post_id: PostId, - new_stickied: bool, - ) -> Result { - use crate::schema::post::dsl::*; - diesel::update(post.find(post_id)) - .set(stickied.eq(new_stickied)) - .get_result::(conn) - } - pub fn is_post_creator(person_id: PersonId, post_creator_id: PersonId) -> bool { person_id == post_creator_id } - pub fn upsert(conn: &mut PgConnection, post_form: &PostForm) -> Result { - use crate::schema::post::dsl::*; - insert_into(post) - .values(post_form) - .on_conflict(ap_id) - .do_update() - .set(post_form) - .get_result::(conn) - } pub fn read_from_apub_id(conn: &mut PgConnection, object_id: Url) -> Result, Error> { use crate::schema::post::dsl::*; let object_id: DbUrl = object_id.into(); @@ -334,7 +280,8 @@ impl DeleteableOrRemoveable for Post { mod tests { use crate::{ source::{ - community::{Community, CommunityForm}, + community::{Community, CommunityInsertForm}, + instance::Instance, person::*, post::*, }, @@ -348,29 +295,30 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "jim".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("jim".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let new_community = CommunityForm { - name: "test community_3".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community_3".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); @@ -445,13 +393,19 @@ mod tests { }; let read_post = Post::read(conn, inserted_post.id).unwrap(); - let updated_post = Post::update(conn, inserted_post.id, &new_post).unwrap(); + + let new_post_update = PostUpdateForm::builder() + .name(Some("A test post".into())) + .build(); + let updated_post = Post::update(conn, inserted_post.id, &new_post_update).unwrap(); + let like_removed = PostLike::remove(conn, inserted_person.id, inserted_post.id).unwrap(); let saved_removed = PostSaved::unsave(conn, &post_saved_form).unwrap(); let read_removed = PostRead::mark_as_unread(conn, &post_read_form).unwrap(); let num_deleted = Post::delete(conn, inserted_post.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); Person::delete(conn, inserted_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_post, read_post); assert_eq!(expected_post, inserted_post); diff --git a/crates/db_schema/src/impls/private_message.rs b/crates/db_schema/src/impls/private_message.rs index 2ec1702e..80818ee3 100644 --- a/crates/db_schema/src/impls/private_message.rs +++ b/crates/db_schema/src/impls/private_message.rs @@ -2,38 +2,38 @@ use crate::{ newtypes::{DbUrl, PersonId, PrivateMessageId}, source::private_message::*, traits::{Crud, DeleteableOrRemoveable}, - utils::naive_now, }; use diesel::{dsl::*, result::Error, *}; use lemmy_utils::error::LemmyError; use url::Url; impl Crud for PrivateMessage { - type Form = PrivateMessageForm; + type InsertForm = PrivateMessageInsertForm; + type UpdateForm = PrivateMessageUpdateForm; type IdType = PrivateMessageId; fn read(conn: &mut PgConnection, private_message_id: PrivateMessageId) -> Result { use crate::schema::private_message::dsl::*; private_message.find(private_message_id).first::(conn) } - fn create( - conn: &mut PgConnection, - private_message_form: &PrivateMessageForm, - ) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::private_message::dsl::*; insert_into(private_message) - .values(private_message_form) + .values(form) + .on_conflict(ap_id) + .do_update() + .set(form) .get_result::(conn) } fn update( conn: &mut PgConnection, private_message_id: PrivateMessageId, - private_message_form: &PrivateMessageForm, + form: &Self::UpdateForm, ) -> Result { use crate::schema::private_message::dsl::*; diesel::update(private_message.find(private_message_id)) - .set(private_message_form) + .set(form) .get_result::(conn) } fn delete(conn: &mut PgConnection, pm_id: Self::IdType) -> Result { @@ -43,51 +43,6 @@ impl Crud for PrivateMessage { } impl PrivateMessage { - pub fn update_ap_id( - conn: &mut PgConnection, - private_message_id: PrivateMessageId, - apub_id: DbUrl, - ) -> Result { - use crate::schema::private_message::dsl::*; - - diesel::update(private_message.find(private_message_id)) - .set(ap_id.eq(apub_id)) - .get_result::(conn) - } - - pub fn update_content( - conn: &mut PgConnection, - private_message_id: PrivateMessageId, - new_content: &str, - ) -> Result { - use crate::schema::private_message::dsl::*; - diesel::update(private_message.find(private_message_id)) - .set((content.eq(new_content), updated.eq(naive_now()))) - .get_result::(conn) - } - - pub fn update_deleted( - conn: &mut PgConnection, - private_message_id: PrivateMessageId, - new_deleted: bool, - ) -> Result { - use crate::schema::private_message::dsl::*; - diesel::update(private_message.find(private_message_id)) - .set(deleted.eq(new_deleted)) - .get_result::(conn) - } - - pub fn update_read( - conn: &mut PgConnection, - private_message_id: PrivateMessageId, - new_read: bool, - ) -> Result { - use crate::schema::private_message::dsl::*; - diesel::update(private_message.find(private_message_id)) - .set(read.eq(new_read)) - .get_result::(conn) - } - pub fn mark_all_as_read( conn: &mut PgConnection, for_recipient_id: PersonId, @@ -102,19 +57,6 @@ impl PrivateMessage { .get_results::(conn) } - pub fn upsert( - conn: &mut PgConnection, - private_message_form: &PrivateMessageForm, - ) -> Result { - use crate::schema::private_message::dsl::*; - insert_into(private_message) - .values(private_message_form) - .on_conflict(ap_id) - .do_update() - .set(private_message_form) - .get_result::(conn) - } - pub fn read_from_apub_id( conn: &mut PgConnection, object_id: Url, @@ -141,7 +83,7 @@ impl DeleteableOrRemoveable for PrivateMessage { #[cfg(test)] mod tests { use crate::{ - source::{person::*, private_message::*}, + source::{instance::Instance, person::*, private_message::*}, traits::Crud, utils::establish_unpooled_connection, }; @@ -152,28 +94,29 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let creator_form = PersonForm { - name: "creator_pm".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let creator_form = PersonInsertForm::builder() + .name("creator_pm".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_creator = Person::create(conn, &creator_form).unwrap(); - let recipient_form = PersonForm { - name: "recipient_pm".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let recipient_form = PersonInsertForm::builder() + .name("recipient_pm".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_recipient = Person::create(conn, &recipient_form).unwrap(); - let private_message_form = PrivateMessageForm { - content: "A test private message".into(), - creator_id: inserted_creator.id, - recipient_id: inserted_recipient.id, - ..PrivateMessageForm::default() - }; + let private_message_form = PrivateMessageInsertForm::builder() + .content("A test private message".into()) + .creator_id(inserted_creator.id) + .recipient_id(inserted_recipient.id) + .build(); let inserted_private_message = PrivateMessage::create(conn, &private_message_form).unwrap(); @@ -191,14 +134,34 @@ mod tests { }; let read_private_message = PrivateMessage::read(conn, inserted_private_message.id).unwrap(); - let updated_private_message = - PrivateMessage::update(conn, inserted_private_message.id, &private_message_form).unwrap(); - let deleted_private_message = - PrivateMessage::update_deleted(conn, inserted_private_message.id, true).unwrap(); - let marked_read_private_message = - PrivateMessage::update_read(conn, inserted_private_message.id, true).unwrap(); + + let private_message_update_form = PrivateMessageUpdateForm::builder() + .content(Some("A test private message".into())) + .build(); + let updated_private_message = PrivateMessage::update( + conn, + inserted_private_message.id, + &private_message_update_form, + ) + .unwrap(); + + let deleted_private_message = PrivateMessage::update( + conn, + inserted_private_message.id, + &PrivateMessageUpdateForm::builder() + .deleted(Some(true)) + .build(), + ) + .unwrap(); + let marked_read_private_message = PrivateMessage::update( + conn, + inserted_private_message.id, + &PrivateMessageUpdateForm::builder().read(Some(true)).build(), + ) + .unwrap(); Person::delete(conn, inserted_creator.id).unwrap(); Person::delete(conn, inserted_recipient.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); assert_eq!(expected_private_message, read_private_message); assert_eq!(expected_private_message, updated_private_message); diff --git a/crates/db_schema/src/impls/registration_application.rs b/crates/db_schema/src/impls/registration_application.rs index 6868a1da..8004abc6 100644 --- a/crates/db_schema/src/impls/registration_application.rs +++ b/crates/db_schema/src/impls/registration_application.rs @@ -2,9 +2,10 @@ use crate::{newtypes::LocalUserId, source::registration_application::*, traits:: use diesel::{insert_into, result::Error, ExpressionMethods, PgConnection, QueryDsl, RunQueryDsl}; impl Crud for RegistrationApplication { - type Form = RegistrationApplicationForm; + type InsertForm = RegistrationApplicationInsertForm; + type UpdateForm = RegistrationApplicationUpdateForm; type IdType = i32; - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::registration_application::dsl::*; insert_into(registration_application) .values(form) @@ -16,7 +17,11 @@ impl Crud for RegistrationApplication { registration_application.find(id_).first::(conn) } - fn update(conn: &mut PgConnection, id_: Self::IdType, form: &Self::Form) -> Result { + fn update( + conn: &mut PgConnection, + id_: Self::IdType, + form: &Self::UpdateForm, + ) -> Result { use crate::schema::registration_application::dsl::*; diesel::update(registration_application.find(id_)) .set(form) diff --git a/crates/db_schema/src/impls/site.rs b/crates/db_schema/src/impls/site.rs index ef80b6f6..bea9a21c 100644 --- a/crates/db_schema/src/impls/site.rs +++ b/crates/db_schema/src/impls/site.rs @@ -7,17 +7,22 @@ use diesel::{dsl::*, result::Error, *}; use url::Url; impl Crud for Site { - type Form = SiteForm; + type InsertForm = SiteInsertForm; + type UpdateForm = SiteUpdateForm; type IdType = SiteId; fn read(conn: &mut PgConnection, _site_id: SiteId) -> Result { use crate::schema::site::dsl::*; site.first::(conn) } - fn create(conn: &mut PgConnection, new_site: &SiteForm) -> Result { + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result { use crate::schema::site::dsl::*; + let site_ = insert_into(site) - .values(new_site) + .values(form) + .on_conflict(actor_id) + .do_update() + .set(form) .get_result::(conn)?; // initialize with all languages @@ -25,7 +30,11 @@ impl Crud for Site { Ok(site_) } - fn update(conn: &mut PgConnection, site_id: SiteId, new_site: &SiteForm) -> Result { + fn update( + conn: &mut PgConnection, + site_id: SiteId, + new_site: &Self::UpdateForm, + ) -> Result { use crate::schema::site::dsl::*; diesel::update(site.find(site_id)) .set(new_site) @@ -39,21 +48,6 @@ impl Crud for Site { } impl Site { - pub fn read_local(conn: &mut PgConnection) -> Result { - use crate::schema::site::dsl::*; - site.order_by(id).first::(conn) - } - - pub fn upsert(conn: &mut PgConnection, site_form: &SiteForm) -> Result { - use crate::schema::site::dsl::*; - insert_into(site) - .values(site_form) - .on_conflict(actor_id) - .do_update() - .set(site_form) - .get_result::(conn) - } - pub fn read_from_apub_id(conn: &mut PgConnection, object_id: Url) -> Result, Error> { use crate::schema::site::dsl::*; let object_id: DbUrl = object_id.into(); @@ -66,6 +60,7 @@ impl Site { ) } + // TODO this needs fixed pub fn read_remote_sites(conn: &mut PgConnection) -> Result, Error> { use crate::schema::site::dsl::*; site.order_by(id).offset(1).get_results::(conn) diff --git a/crates/db_schema/src/lib.rs b/crates/db_schema/src/lib.rs index 086c7029..a859b2aa 100644 --- a/crates/db_schema/src/lib.rs +++ b/crates/db_schema/src/lib.rs @@ -1,3 +1,5 @@ +#![recursion_limit = "256"] + #[cfg(feature = "full")] #[macro_use] extern crate diesel; diff --git a/crates/db_schema/src/newtypes.rs b/crates/db_schema/src/newtypes.rs index d0287e15..14768837 100644 --- a/crates/db_schema/src/newtypes.rs +++ b/crates/db_schema/src/newtypes.rs @@ -97,6 +97,14 @@ pub struct CommunityLanguageId(pub i32); #[cfg_attr(feature = "full", derive(DieselNewType))] pub struct CommentReplyId(i32); +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType))] +pub struct InstanceId(i32); + +#[derive(Debug, Copy, Clone, Hash, Eq, PartialEq, Serialize, Deserialize, Default)] +#[cfg_attr(feature = "full", derive(DieselNewType))] +pub struct LocalSiteId(i32); + #[repr(transparent)] #[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)] #[cfg_attr(feature = "full", derive(AsExpression, FromSqlRow))] diff --git a/crates/db_schema/src/schema.rs b/crates/db_schema/src/schema.rs index cc72bb9b..9f0b1eb9 100644 --- a/crates/db_schema/src/schema.rs +++ b/crates/db_schema/src/schema.rs @@ -100,6 +100,7 @@ table! { shared_inbox_url -> Nullable, hidden -> Bool, posting_restricted_to_mods -> Bool, + instance_id -> Int4, } } @@ -313,6 +314,7 @@ table! { admin -> Bool, bot_account -> Bool, ban_expires -> Nullable, + instance_id -> Int4, } } @@ -485,27 +487,15 @@ table! { sidebar -> Nullable, published -> Timestamp, updated -> Nullable, - enable_downvotes -> Bool, - open_registration -> Bool, - enable_nsfw -> Bool, icon -> Nullable, banner -> Nullable, description -> Nullable, - community_creation_admin_only -> Bool, - require_email_verification -> Bool, - require_application -> Bool, - application_question -> Nullable, - private_instance -> Bool, actor_id -> Text, last_refreshed_at -> Timestamp, inbox_url -> Text, private_key -> Nullable, public_key -> Text, - default_theme -> Text, - default_post_listing_type -> Text, - legal_information -> Nullable, - application_email_admins -> Bool, - hide_modlog_mod_names -> Bool, + instance_id -> Int4, } } @@ -651,6 +641,86 @@ table! { } } +table! { + instance(id) { + id -> Int4, + domain -> Text, + published -> Timestamp, + updated -> Nullable, + } +} + +table! { + federation_allowlist(id) { + id -> Int4, + instance_id -> Int4, + published -> Timestamp, + updated -> Nullable, + } +} + +table! { + federation_blocklist(id) { + id -> Int4, + instance_id -> Int4, + published -> Timestamp, + updated -> Nullable, + } +} + +table! { + local_site(id) { + id -> Int4, + site_id -> Int4, + site_setup -> Bool, + enable_downvotes -> Bool, + open_registration -> Bool, + enable_nsfw -> Bool, + community_creation_admin_only -> Bool, + require_email_verification -> Bool, + require_application -> Bool, + application_question -> Nullable, + private_instance -> Bool, + default_theme -> Text, + default_post_listing_type -> Text, + legal_information -> Nullable, + hide_modlog_mod_names -> Bool, + application_email_admins -> Bool, + slur_filter_regex -> Nullable, + actor_name_max_length -> Int4, + federation_enabled -> Bool, + federation_debug -> Bool, + federation_strict_allowlist -> Bool, + federation_http_fetch_retry_limit -> Int4, + federation_worker_count -> Int4, + captcha_enabled -> Bool, + captcha_difficulty -> Text, + published -> Timestamp, + updated -> Nullable, + } +} + +table! { + local_site_rate_limit(id) { + id -> Int4, + local_site_id -> Int4, + message -> Int4, + message_per_second-> Int4, + post -> Int4, + post_per_second -> Int4, + register -> Int4, + register_per_second -> Int4, + image -> Int4, + image_per_second -> Int4, + comment -> Int4, + comment_per_second -> Int4, + search -> Int4, + search_per_second -> Int4, + published -> Timestamp, + updated -> Nullable, + } +} + joinable!(person_block -> person (person_id)); joinable!(comment -> person (creator_id)); @@ -727,6 +797,14 @@ joinable!(admin_purge_person -> person (admin_person_id)); joinable!(admin_purge_post -> community (community_id)); joinable!(admin_purge_post -> person (admin_person_id)); +joinable!(site -> instance (instance_id)); +joinable!(person -> instance (instance_id)); +joinable!(community -> instance (instance_id)); +joinable!(federation_allowlist -> instance (instance_id)); +joinable!(federation_blocklist -> instance (instance_id)); +joinable!(local_site -> site (site_id)); +joinable!(local_site_rate_limit -> local_site (local_site_id)); + allow_tables_to_appear_in_same_query!( activity, comment, @@ -780,4 +858,9 @@ allow_tables_to_appear_in_same_query!( local_user_language, site_language, community_language, + instance, + federation_allowlist, + federation_blocklist, + local_site, + local_site_rate_limit, ); diff --git a/crates/db_schema/src/source/activity.rs b/crates/db_schema/src/source/activity.rs index 2d256a5b..55d7d6fb 100644 --- a/crates/db_schema/src/source/activity.rs +++ b/crates/db_schema/src/source/activity.rs @@ -14,12 +14,21 @@ pub struct Activity { pub sensitive: Option, } -#[derive(Insertable, AsChangeset)] +#[derive(Insertable)] #[diesel(table_name = activity)] -pub struct ActivityForm { +pub struct ActivityInsertForm { pub data: Value, pub local: Option, pub updated: Option, pub ap_id: DbUrl, - pub sensitive: bool, + pub sensitive: Option, +} + +#[derive(AsChangeset)] +#[diesel(table_name = activity)] +pub struct ActivityUpdateForm { + pub data: Option, + pub local: Option, + pub updated: Option>, + pub sensitive: Option>, } diff --git a/crates/db_schema/src/source/comment.rs b/crates/db_schema/src/source/comment.rs index d9117bae..5b7ab085 100644 --- a/crates/db_schema/src/source/comment.rs +++ b/crates/db_schema/src/source/comment.rs @@ -1,6 +1,7 @@ use crate::newtypes::{CommentId, DbUrl, LanguageId, LtreeDef, PersonId, PostId}; use diesel_ltree::Ltree; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::{comment, comment_like, comment_saved}; @@ -26,12 +27,16 @@ pub struct Comment { pub language_id: LanguageId, } -#[derive(Clone, Default)] +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = comment))] -pub struct CommentForm { +pub struct CommentInsertForm { + #[builder(!default)] pub creator_id: PersonId, + #[builder(!default)] pub post_id: PostId, + #[builder(!default)] pub content: String, pub removed: Option, pub published: Option, @@ -43,6 +48,22 @@ pub struct CommentForm { pub language_id: Option, } +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = comment))] +pub struct CommentUpdateForm { + pub content: Option, + pub removed: Option, + // Don't use a default naive_now here, because the create function does a lot of comment updates + pub updated: Option>, + pub deleted: Option, + pub ap_id: Option, + pub local: Option, + pub distinguished: Option, + pub language_id: Option, +} + #[derive(PartialEq, Eq, Debug, Clone)] #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))] #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))] diff --git a/crates/db_schema/src/source/comment_reply.rs b/crates/db_schema/src/source/comment_reply.rs index 737d8df6..8b7fe51b 100644 --- a/crates/db_schema/src/source/comment_reply.rs +++ b/crates/db_schema/src/source/comment_reply.rs @@ -19,8 +19,14 @@ pub struct CommentReply { #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = comment_reply))] -pub struct CommentReplyForm { +pub struct CommentReplyInsertForm { pub recipient_id: PersonId, pub comment_id: CommentId, pub read: Option, } + +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = comment_reply))] +pub struct CommentReplyUpdateForm { + pub read: Option, +} diff --git a/crates/db_schema/src/source/community.rs b/crates/db_schema/src/source/community.rs index 074141cc..6d988ae7 100644 --- a/crates/db_schema/src/source/community.rs +++ b/crates/db_schema/src/source/community.rs @@ -1,5 +1,6 @@ -use crate::newtypes::{CommunityId, DbUrl, PersonId}; +use crate::newtypes::{CommunityId, DbUrl, InstanceId, PersonId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::{community, community_follower, community_moderator, community_person_ban}; @@ -29,6 +30,7 @@ pub struct Community { pub shared_inbox_url: Option, pub hidden: bool, pub posting_restricted_to_mods: bool, + pub instance_id: InstanceId, } /// A safe representation of community, without the sensitive info @@ -51,15 +53,19 @@ pub struct CommunitySafe { pub banner: Option, pub hidden: bool, pub posting_restricted_to_mods: bool, + pub instance_id: InstanceId, } -#[derive(Debug, Default)] +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = community))] -pub struct CommunityForm { +pub struct CommunityInsertForm { + #[builder(!default)] pub name: String, + #[builder(!default)] pub title: String, - pub description: Option>, + pub description: Option, pub removed: Option, pub published: Option, pub updated: Option, @@ -67,8 +73,36 @@ pub struct CommunityForm { pub nsfw: Option, pub actor_id: Option, pub local: Option, - pub private_key: Option>, + pub private_key: Option, + pub public_key: String, + pub last_refreshed_at: Option, + pub icon: Option, + pub banner: Option, + pub followers_url: Option, + pub inbox_url: Option, + pub shared_inbox_url: Option, + pub hidden: Option, + pub posting_restricted_to_mods: Option, + #[builder(!default)] + pub instance_id: InstanceId, +} + +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = community))] +pub struct CommunityUpdateForm { + pub title: Option, + pub description: Option>, + pub removed: Option, + pub published: Option, + pub updated: Option>, + pub deleted: Option, + pub nsfw: Option, + pub actor_id: Option, + pub local: Option, pub public_key: Option, + pub private_key: Option>, pub last_refreshed_at: Option, pub icon: Option>, pub banner: Option>, diff --git a/crates/db_schema/src/source/federation_allowlist.rs b/crates/db_schema/src/source/federation_allowlist.rs new file mode 100644 index 00000000..5ca5927f --- /dev/null +++ b/crates/db_schema/src/source/federation_allowlist.rs @@ -0,0 +1,28 @@ +use crate::newtypes::InstanceId; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +#[cfg(feature = "full")] +use crate::schema::federation_allowlist; + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::instance::Instance)) +)] +#[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))] +pub struct FederationAllowList { + pub id: i32, + pub instance_id: InstanceId, + pub published: chrono::NaiveDateTime, + pub updated: Option, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = federation_allowlist))] +pub struct FederationAllowListForm { + pub instance_id: InstanceId, + pub updated: Option, +} diff --git a/crates/db_schema/src/source/federation_blocklist.rs b/crates/db_schema/src/source/federation_blocklist.rs new file mode 100644 index 00000000..70f7a085 --- /dev/null +++ b/crates/db_schema/src/source/federation_blocklist.rs @@ -0,0 +1,28 @@ +use crate::newtypes::InstanceId; +use serde::{Deserialize, Serialize}; +use std::fmt::Debug; + +#[cfg(feature = "full")] +use crate::schema::federation_blocklist; + +#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Associations, Identifiable))] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::instance::Instance)) +)] +#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))] +pub struct FederationBlockList { + pub id: i32, + pub instance_id: InstanceId, + pub published: chrono::NaiveDateTime, + pub updated: Option, +} + +#[derive(Clone, Default)] +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = federation_blocklist))] +pub struct FederationBlockListForm { + pub instance_id: InstanceId, + pub updated: Option, +} diff --git a/crates/db_schema/src/source/instance.rs b/crates/db_schema/src/source/instance.rs new file mode 100644 index 00000000..0b86f9c6 --- /dev/null +++ b/crates/db_schema/src/source/instance.rs @@ -0,0 +1,22 @@ +use crate::newtypes::InstanceId; +use std::fmt::Debug; + +#[cfg(feature = "full")] +use crate::schema::instance; + +#[derive(PartialEq, Eq, Debug)] +#[cfg_attr(feature = "full", derive(Queryable, Identifiable))] +#[cfg_attr(feature = "full", diesel(table_name = instance))] +pub struct Instance { + pub id: InstanceId, + pub domain: String, + pub published: chrono::NaiveDateTime, + pub updated: Option, +} + +#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = instance))] +pub struct InstanceForm { + pub domain: String, + pub updated: Option, +} diff --git a/crates/db_schema/src/source/local_site.rs b/crates/db_schema/src/source/local_site.rs new file mode 100644 index 00000000..443923a4 --- /dev/null +++ b/crates/db_schema/src/source/local_site.rs @@ -0,0 +1,103 @@ +use crate::newtypes::{LocalSiteId, SiteId}; +use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; + +#[cfg(feature = "full")] +use crate::schema::local_site; + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Identifiable))] +#[cfg_attr(feature = "full", diesel(table_name = local_site))] +#[cfg_attr(feature = "full", diesel(belongs_to(crate::source::site::Site)))] +pub struct LocalSite { + pub id: LocalSiteId, + pub site_id: SiteId, + pub site_setup: bool, + pub enable_downvotes: bool, + pub open_registration: bool, + pub enable_nsfw: bool, + pub community_creation_admin_only: bool, + pub require_email_verification: bool, + pub require_application: bool, + pub application_question: Option, + pub private_instance: bool, + pub default_theme: String, + pub default_post_listing_type: String, + pub legal_information: Option, + pub hide_modlog_mod_names: bool, + pub application_email_admins: bool, + pub slur_filter_regex: Option, + pub actor_name_max_length: i32, + pub federation_enabled: bool, + pub federation_debug: bool, + pub federation_strict_allowlist: bool, + pub federation_http_fetch_retry_limit: i32, + pub federation_worker_count: i32, + pub captcha_enabled: bool, + pub captcha_difficulty: String, + pub published: chrono::NaiveDateTime, + pub updated: Option, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(Insertable))] +#[cfg_attr(feature = "full", diesel(table_name = local_site))] +pub struct LocalSiteInsertForm { + #[builder(!default)] + pub site_id: SiteId, + pub site_setup: Option, + pub enable_downvotes: Option, + pub open_registration: Option, + pub enable_nsfw: Option, + pub community_creation_admin_only: Option, + pub require_email_verification: Option, + pub require_application: Option, + pub application_question: Option, + pub private_instance: Option, + pub default_theme: Option, + pub default_post_listing_type: Option, + pub legal_information: Option, + pub hide_modlog_mod_names: Option, + pub application_email_admins: Option, + pub slur_filter_regex: Option, + pub actor_name_max_length: Option, + pub federation_enabled: Option, + pub federation_debug: Option, + pub federation_strict_allowlist: Option, + pub federation_http_fetch_retry_limit: Option, + pub federation_worker_count: Option, + pub captcha_enabled: Option, + pub captcha_difficulty: Option, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = local_site))] +pub struct LocalSiteUpdateForm { + pub site_setup: Option, + pub enable_downvotes: Option, + pub open_registration: Option, + pub enable_nsfw: Option, + pub community_creation_admin_only: Option, + pub require_email_verification: Option, + pub require_application: Option, + pub application_question: Option>, + pub private_instance: Option, + pub default_theme: Option, + pub default_post_listing_type: Option, + pub legal_information: Option>, + pub hide_modlog_mod_names: Option, + pub application_email_admins: Option, + pub slur_filter_regex: Option>, + pub actor_name_max_length: Option, + pub federation_enabled: Option, + pub federation_debug: Option, + pub federation_strict_allowlist: Option, + pub federation_http_fetch_retry_limit: Option, + pub federation_worker_count: Option, + pub captcha_enabled: Option, + pub captcha_difficulty: Option, + pub updated: Option>, +} diff --git a/crates/db_schema/src/source/local_site_rate_limit.rs b/crates/db_schema/src/source/local_site_rate_limit.rs new file mode 100644 index 00000000..bd890137 --- /dev/null +++ b/crates/db_schema/src/source/local_site_rate_limit.rs @@ -0,0 +1,73 @@ +use crate::newtypes::LocalSiteId; +use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; + +#[cfg(feature = "full")] +use crate::schema::local_site_rate_limit; + +#[derive(PartialEq, Eq, Debug, Clone, Serialize, Deserialize)] +#[cfg_attr(feature = "full", derive(Queryable, Identifiable))] +#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))] +#[cfg_attr( + feature = "full", + diesel(belongs_to(crate::source::local_site::LocalSite)) +)] +pub struct LocalSiteRateLimit { + pub id: i32, + pub local_site_id: LocalSiteId, + pub message: i32, + pub message_per_second: i32, + pub post: i32, + pub post_per_second: i32, + pub register: i32, + pub register_per_second: i32, + pub image: i32, + pub image_per_second: i32, + pub comment: i32, + pub comment_per_second: i32, + pub search: i32, + pub search_per_second: i32, + pub published: chrono::NaiveDateTime, + pub updated: Option, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(Insertable))] +#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))] +pub struct LocalSiteRateLimitInsertForm { + #[builder(!default)] + pub local_site_id: LocalSiteId, + pub message: Option, + pub message_per_second: Option, + pub post: Option, + pub post_per_second: Option, + pub register: Option, + pub register_per_second: Option, + pub image: Option, + pub image_per_second: Option, + pub comment: Option, + pub comment_per_second: Option, + pub search: Option, + pub search_per_second: Option, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = local_site_rate_limit))] +pub struct LocalSiteRateLimitUpdateForm { + pub message: Option, + pub message_per_second: Option, + pub post: Option, + pub post_per_second: Option, + pub register: Option, + pub register_per_second: Option, + pub image: Option, + pub image_per_second: Option, + pub comment: Option, + pub comment_per_second: Option, + pub search: Option, + pub search_per_second: Option, + pub updated: Option>, +} diff --git a/crates/db_schema/src/source/local_user.rs b/crates/db_schema/src/source/local_user.rs index 4b3a3414..04fb4b87 100644 --- a/crates/db_schema/src/source/local_user.rs +++ b/crates/db_schema/src/source/local_user.rs @@ -1,5 +1,6 @@ use crate::newtypes::{LocalUserId, PersonId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::local_user; @@ -28,29 +29,6 @@ pub struct LocalUser { pub accepted_application: bool, } -// TODO redo these, check table defaults -#[derive(Clone, Default)] -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] -#[cfg_attr(feature = "full", diesel(table_name = local_user))] -pub struct LocalUserForm { - pub person_id: Option, - pub password_encrypted: Option, - pub email: Option>, - pub show_nsfw: Option, - pub theme: Option, - pub default_sort_type: Option, - pub default_listing_type: Option, - pub interface_language: Option, - pub show_avatars: Option, - pub send_notifications_to_email: Option, - pub show_bot_accounts: Option, - pub show_scores: Option, - pub show_read_posts: Option, - pub show_new_post_notifs: Option, - pub email_verified: Option, - pub accepted_application: Option, -} - /// A local user view that removes password encrypted #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] #[cfg_attr(feature = "full", derive(Queryable, Identifiable))] @@ -74,3 +52,50 @@ pub struct LocalUserSettings { pub email_verified: bool, pub accepted_application: bool, } + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(Insertable))] +#[cfg_attr(feature = "full", diesel(table_name = local_user))] +pub struct LocalUserInsertForm { + #[builder(!default)] + pub person_id: PersonId, + #[builder(!default)] + pub password_encrypted: String, + pub email: Option, + pub show_nsfw: Option, + pub theme: Option, + pub default_sort_type: Option, + pub default_listing_type: Option, + pub interface_language: Option, + pub show_avatars: Option, + pub send_notifications_to_email: Option, + pub show_bot_accounts: Option, + pub show_scores: Option, + pub show_read_posts: Option, + pub show_new_post_notifs: Option, + pub email_verified: Option, + pub accepted_application: Option, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = local_user))] +pub struct LocalUserUpdateForm { + pub password_encrypted: Option, + pub email: Option>, + pub show_nsfw: Option, + pub theme: Option, + pub default_sort_type: Option, + pub default_listing_type: Option, + pub interface_language: Option, + pub show_avatars: Option, + pub send_notifications_to_email: Option, + pub show_bot_accounts: Option, + pub show_scores: Option, + pub show_read_posts: Option, + pub show_new_post_notifs: Option, + pub email_verified: Option, + pub accepted_application: Option, +} diff --git a/crates/db_schema/src/source/mod.rs b/crates/db_schema/src/source/mod.rs index 676acd8f..b8e091fc 100644 --- a/crates/db_schema/src/source/mod.rs +++ b/crates/db_schema/src/source/mod.rs @@ -7,7 +7,12 @@ pub mod comment_report; pub mod community; pub mod community_block; pub mod email_verification; +pub mod federation_allowlist; +pub mod federation_blocklist; +pub mod instance; pub mod language; +pub mod local_site; +pub mod local_site_rate_limit; pub mod local_user; pub mod moderator; pub mod password_reset_request; diff --git a/crates/db_schema/src/source/person.rs b/crates/db_schema/src/source/person.rs index 5d9d603d..0d2706c5 100644 --- a/crates/db_schema/src/source/person.rs +++ b/crates/db_schema/src/source/person.rs @@ -1,5 +1,6 @@ -use crate::newtypes::{DbUrl, PersonId}; +use crate::newtypes::{DbUrl, InstanceId, PersonId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::person; @@ -29,6 +30,7 @@ pub struct Person { pub admin: bool, pub bot_account: bool, pub ban_expires: Option, + pub instance_id: InstanceId, } /// A safe representation of person, without the sensitive info @@ -54,23 +56,54 @@ pub struct PersonSafe { pub admin: bool, pub bot_account: bool, pub ban_expires: Option, + pub instance_id: InstanceId, } -#[derive(Clone, Default)] +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = person))] -pub struct PersonForm { +pub struct PersonInsertForm { + #[builder(!default)] pub name: String, - pub display_name: Option>, - pub avatar: Option>, + #[builder(!default)] + pub public_key: String, + #[builder(!default)] + pub instance_id: InstanceId, + pub display_name: Option, + pub avatar: Option, pub banned: Option, pub published: Option, pub updated: Option, pub actor_id: Option, + pub bio: Option, + pub local: Option, + pub private_key: Option, + pub last_refreshed_at: Option, + pub banner: Option, + pub deleted: Option, + pub inbox_url: Option, + pub shared_inbox_url: Option, + pub matrix_user_id: Option, + pub admin: Option, + pub bot_account: Option, + pub ban_expires: Option, +} + +#[derive(Clone, TypedBuilder)] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = person))] +#[builder(field_defaults(default))] +pub struct PersonUpdateForm { + pub display_name: Option>, + pub avatar: Option>, + pub banned: Option, + pub updated: Option>, + pub actor_id: Option, pub bio: Option>, pub local: Option, - pub private_key: Option>, pub public_key: Option, + pub private_key: Option>, pub last_refreshed_at: Option, pub banner: Option>, pub deleted: Option, diff --git a/crates/db_schema/src/source/person_mention.rs b/crates/db_schema/src/source/person_mention.rs index a12563e5..b4a2ce59 100644 --- a/crates/db_schema/src/source/person_mention.rs +++ b/crates/db_schema/src/source/person_mention.rs @@ -18,8 +18,14 @@ pub struct PersonMention { #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = person_mention))] -pub struct PersonMentionForm { +pub struct PersonMentionInsertForm { pub recipient_id: PersonId, pub comment_id: CommentId, pub read: Option, } + +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = person_mention))] +pub struct PersonMentionUpdateForm { + pub read: Option, +} diff --git a/crates/db_schema/src/source/post.rs b/crates/db_schema/src/source/post.rs index e9f383fe..9af6a3f2 100644 --- a/crates/db_schema/src/source/post.rs +++ b/crates/db_schema/src/source/post.rs @@ -1,5 +1,6 @@ use crate::newtypes::{CommunityId, DbUrl, LanguageId, PersonId, PostId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::{post, post_like, post_read, post_saved}; @@ -30,20 +31,48 @@ pub struct Post { pub language_id: LanguageId, } -#[derive(Default)] +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = post))] -pub struct PostForm { +pub struct PostInsertForm { + #[builder(!default)] pub name: String, + #[builder(!default)] pub creator_id: PersonId, + #[builder(!default)] pub community_id: CommunityId, pub nsfw: Option, + pub url: Option, + pub body: Option, + pub removed: Option, + pub locked: Option, + pub updated: Option, + pub published: Option, + pub deleted: Option, + pub stickied: Option, + pub embed_title: Option, + pub embed_description: Option, + pub embed_video_url: Option, + pub thumbnail_url: Option, + pub ap_id: Option, + pub local: Option, + pub language_id: Option, +} + +#[derive(Debug, Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = post))] +pub struct PostUpdateForm { + pub name: Option, + pub nsfw: Option, pub url: Option>, pub body: Option>, pub removed: Option, pub locked: Option, pub published: Option, - pub updated: Option, + pub updated: Option>, pub deleted: Option, pub stickied: Option, pub embed_title: Option>, diff --git a/crates/db_schema/src/source/private_message.rs b/crates/db_schema/src/source/private_message.rs index 9b2cb342..f6e969f3 100644 --- a/crates/db_schema/src/source/private_message.rs +++ b/crates/db_schema/src/source/private_message.rs @@ -1,5 +1,6 @@ use crate::newtypes::{DbUrl, PersonId, PrivateMessageId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::private_message; @@ -24,12 +25,16 @@ pub struct PrivateMessage { pub local: bool, } -#[derive(Default)] +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = private_message))] -pub struct PrivateMessageForm { +pub struct PrivateMessageInsertForm { + #[builder(!default)] pub creator_id: PersonId, + #[builder(!default)] pub recipient_id: PersonId, + #[builder(!default)] pub content: String, pub deleted: Option, pub read: Option, @@ -38,3 +43,17 @@ pub struct PrivateMessageForm { pub ap_id: Option, pub local: Option, } + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = private_message))] +pub struct PrivateMessageUpdateForm { + pub content: Option, + pub deleted: Option, + pub read: Option, + pub published: Option, + pub updated: Option>, + pub ap_id: Option, + pub local: Option, +} diff --git a/crates/db_schema/src/source/registration_application.rs b/crates/db_schema/src/source/registration_application.rs index ecf4eb3e..40f00bdf 100644 --- a/crates/db_schema/src/source/registration_application.rs +++ b/crates/db_schema/src/source/registration_application.rs @@ -16,12 +16,16 @@ pub struct RegistrationApplication { pub published: chrono::NaiveDateTime, } -#[derive(Default)] -#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] +#[cfg_attr(feature = "full", derive(Insertable))] #[cfg_attr(feature = "full", diesel(table_name = registration_application))] -pub struct RegistrationApplicationForm { - pub local_user_id: Option, - pub answer: Option, - pub admin_id: Option, +pub struct RegistrationApplicationInsertForm { + pub local_user_id: LocalUserId, + pub answer: String, +} + +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = registration_application))] +pub struct RegistrationApplicationUpdateForm { + pub admin_id: Option>, pub deny_reason: Option>, } diff --git a/crates/db_schema/src/source/site.rs b/crates/db_schema/src/source/site.rs index d550ab38..8c4a5b1c 100644 --- a/crates/db_schema/src/source/site.rs +++ b/crates/db_schema/src/source/site.rs @@ -1,5 +1,6 @@ -use crate::newtypes::{DbUrl, SiteId}; +use crate::newtypes::{DbUrl, InstanceId, SiteId}; use serde::{Deserialize, Serialize}; +use typed_builder::TypedBuilder; #[cfg(feature = "full")] use crate::schema::site; @@ -13,56 +14,53 @@ pub struct Site { pub sidebar: Option, pub published: chrono::NaiveDateTime, pub updated: Option, - pub enable_downvotes: bool, - pub open_registration: bool, - pub enable_nsfw: bool, pub icon: Option, pub banner: Option, pub description: Option, - pub community_creation_admin_only: bool, - pub require_email_verification: bool, - pub require_application: bool, - pub application_question: Option, - pub private_instance: bool, pub actor_id: DbUrl, pub last_refreshed_at: chrono::NaiveDateTime, pub inbox_url: DbUrl, pub private_key: Option, pub public_key: String, - pub default_theme: String, - pub default_post_listing_type: String, - pub legal_information: Option, - pub application_email_admins: bool, - pub hide_modlog_mod_names: bool, + pub instance_id: InstanceId, } -#[derive(Default)] +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] #[cfg_attr(feature = "full", derive(Insertable, AsChangeset))] #[cfg_attr(feature = "full", diesel(table_name = site))] -pub struct SiteForm { +pub struct SiteInsertForm { + #[builder(!default)] pub name: String, - pub sidebar: Option>, + pub sidebar: Option, pub updated: Option, - pub enable_downvotes: Option, - pub open_registration: Option, - pub enable_nsfw: Option, + pub icon: Option, + pub banner: Option, + pub description: Option, + pub actor_id: Option, + pub last_refreshed_at: Option, + pub inbox_url: Option, + pub private_key: Option, + pub public_key: Option, + #[builder(!default)] + pub instance_id: InstanceId, +} + +#[derive(Clone, TypedBuilder)] +#[builder(field_defaults(default))] +#[cfg_attr(feature = "full", derive(AsChangeset))] +#[cfg_attr(feature = "full", diesel(table_name = site))] +pub struct SiteUpdateForm { + pub name: Option, + pub sidebar: Option>, + pub updated: Option>, // when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. pub icon: Option>, pub banner: Option>, pub description: Option>, - pub community_creation_admin_only: Option, - pub require_email_verification: Option, - pub require_application: Option, - pub application_question: Option>, - pub private_instance: Option, pub actor_id: Option, pub last_refreshed_at: Option, pub inbox_url: Option, pub private_key: Option>, pub public_key: Option, - pub default_theme: Option, - pub default_post_listing_type: Option, - pub legal_information: Option>, - pub application_email_admins: Option, - pub hide_modlog_mod_names: Option, } diff --git a/crates/db_schema/src/traits.rs b/crates/db_schema/src/traits.rs index 102d0c0b..6e6d2c70 100644 --- a/crates/db_schema/src/traits.rs +++ b/crates/db_schema/src/traits.rs @@ -2,15 +2,21 @@ use crate::newtypes::{CommunityId, DbUrl, PersonId}; use diesel::{result::Error, PgConnection}; pub trait Crud { - type Form; + type InsertForm; + type UpdateForm; type IdType; - fn create(conn: &mut PgConnection, form: &Self::Form) -> Result + fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result where Self: Sized; fn read(conn: &mut PgConnection, id: Self::IdType) -> Result where Self: Sized; - fn update(conn: &mut PgConnection, id: Self::IdType, form: &Self::Form) -> Result + /// when you want to null out a column, you have to send Some(None)), since sending None means you just don't want to update that column. + fn update( + conn: &mut PgConnection, + id: Self::IdType, + form: &Self::UpdateForm, + ) -> Result where Self: Sized; fn delete(_conn: &mut PgConnection, _id: Self::IdType) -> Result diff --git a/crates/db_schema/src/utils.rs b/crates/db_schema/src/utils.rs index 798786b0..d20b32d8 100644 --- a/crates/db_schema/src/utils.rs +++ b/crates/db_schema/src/utils.rs @@ -100,6 +100,20 @@ pub fn diesel_option_overwrite_to_url( } } +pub fn diesel_option_overwrite_to_url_create( + opt: &Option, +) -> Result, LemmyError> { + match opt.as_ref().map(|s| s.as_str()) { + // An empty string is nothing + Some("") => Ok(None), + Some(str_url) => match Url::parse(str_url) { + Ok(url) => Ok(Some(url.into())), + Err(e) => Err(LemmyError::from_error_message(e, "invalid_url")), + }, + None => Ok(None), + } +} + pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); pub fn establish_unpooled_connection() -> PgConnection { diff --git a/crates/db_views/Cargo.toml b/crates/db_views/Cargo.toml index 3046c4e7..1a9e31eb 100644 --- a/crates/db_views/Cargo.toml +++ b/crates/db_views/Cargo.toml @@ -21,6 +21,7 @@ serde = { version = "1.0.145", features = ["derive"] } tracing = { version = "0.1.36", optional = true } diesel_ltree = "0.3.0" typed-builder = "0.10.0" +url = { version = "2.3.1", features = ["serde"] } [dev-dependencies] serial_test = "0.9.0" diff --git a/crates/db_views/src/comment_report_view.rs b/crates/db_views/src/comment_report_view.rs index 50564b0a..e4a6a493 100644 --- a/crates/db_views/src/comment_report_view.rs +++ b/crates/db_views/src/comment_report_view.rs @@ -290,7 +290,7 @@ mod tests { use crate::comment_report_view::{CommentReportQuery, CommentReportView}; use lemmy_db_schema::{ aggregates::structs::CommentAggregates, - source::{comment::*, comment_report::*, community::*, person::*, post::*}, + source::{comment::*, comment_report::*, community::*, instance::Instance, person::*, post::*}, traits::{Crud, Joinable, Reportable}, utils::establish_unpooled_connection, }; @@ -301,37 +301,39 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "timmy_crv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("timmy_crv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_timmy = Person::create(conn, &new_person).unwrap(); - let new_person_2 = PersonForm { - name: "sara_crv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_2 = PersonInsertForm::builder() + .name("sara_crv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_sara = Person::create(conn, &new_person_2).unwrap(); // Add a third person, since new ppl can only report something once. - let new_person_3 = PersonForm { - name: "jessica_crv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_3 = PersonInsertForm::builder() + .name("jessica_crv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_jessica = Person::create(conn, &new_person_3).unwrap(); - let new_community = CommunityForm { - name: "test community crv".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community crv".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); @@ -343,21 +345,19 @@ mod tests { let _inserted_moderator = CommunityModerator::join(conn, &timmy_moderator_form).unwrap(); - let new_post = PostForm { - name: "A test post crv".into(), - creator_id: inserted_timmy.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post crv".into()) + .creator_id(inserted_timmy.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let comment_form = CommentForm { - content: "A test comment 32".into(), - creator_id: inserted_timmy.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form = CommentInsertForm::builder() + .content("A test comment 32".into()) + .creator_id(inserted_timmy.id) + .post_id(inserted_post.id) + .build(); let inserted_comment = Comment::create(conn, &comment_form, None).unwrap(); @@ -405,6 +405,7 @@ mod tests { hidden: false, posting_restricted_to_mods: false, published: inserted_community.published, + instance_id: inserted_instance.id, }, creator: PersonSafe { id: inserted_jessica.id, @@ -425,6 +426,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }, comment_creator: PersonSafe { id: inserted_timmy.id, @@ -445,6 +447,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }, creator_banned_from_community: false, counts: CommentAggregates { @@ -483,6 +486,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }; // Do a batch read of timmys reports @@ -543,6 +547,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }); assert_eq!( @@ -571,5 +576,6 @@ mod tests { Person::delete(conn, inserted_sara.id).unwrap(); Person::delete(conn, inserted_jessica.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_views/src/comment_view.rs b/crates/db_views/src/comment_view.rs index 5a64a298..5a94d37a 100644 --- a/crates/db_views/src/comment_view.rs +++ b/crates/db_views/src/comment_view.rs @@ -396,8 +396,9 @@ mod tests { actor_language::LocalUserLanguage, comment::*, community::*, + instance::Instance, language::Language, - local_user::LocalUserForm, + local_user::LocalUserInsertForm, person::*, person_block::PersonBlockForm, post::*, @@ -409,6 +410,7 @@ mod tests { use serial_test::serial; struct Data { + inserted_instance: Instance, inserted_comment_0: Comment, inserted_comment_1: Comment, inserted_comment_2: Comment, @@ -420,41 +422,41 @@ mod tests { } fn init_data(conn: &mut PgConnection) -> Data { - let new_person = PersonForm { - name: "timmy".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("timmy".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(inserted_person.id), - password_encrypted: Some("".to_string()), - ..Default::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .password_encrypted("".to_string()) + .build(); let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap(); - let new_person_2 = PersonForm { - name: "sara".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_2 = PersonInsertForm::builder() + .name("sara".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person_2 = Person::create(conn, &new_person_2).unwrap(); - let new_community = CommunityForm { - name: "test community 5".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community 5".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); - let new_post = PostForm { - name: "A test post 2".into(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post 2".into()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); @@ -466,65 +468,59 @@ mod tests { // 3 4 // \ // 5 - let comment_form_0 = CommentForm { - content: "Comment 0".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form_0 = CommentInsertForm::builder() + .content("Comment 0".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let inserted_comment_0 = Comment::create(conn, &comment_form_0, None).unwrap(); - let comment_form_1 = CommentForm { - content: "Comment 1, A test blocked comment".into(), - creator_id: inserted_person_2.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form_1 = CommentInsertForm::builder() + .content("Comment 1, A test blocked comment".into()) + .creator_id(inserted_person_2.id) + .post_id(inserted_post.id) + .build(); let inserted_comment_1 = Comment::create(conn, &comment_form_1, Some(&inserted_comment_0.path)).unwrap(); let finnish_id = Language::read_id_from_code(conn, "fi").unwrap(); - let comment_form_2 = CommentForm { - content: "Comment 2".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - language_id: Some(finnish_id), - ..CommentForm::default() - }; + let comment_form_2 = CommentInsertForm::builder() + .content("Comment 2".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .language_id(Some(finnish_id)) + .build(); let inserted_comment_2 = Comment::create(conn, &comment_form_2, Some(&inserted_comment_0.path)).unwrap(); - let comment_form_3 = CommentForm { - content: "Comment 3".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form_3 = CommentInsertForm::builder() + .content("Comment 3".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let _inserted_comment_3 = Comment::create(conn, &comment_form_3, Some(&inserted_comment_1.path)).unwrap(); let polish_id = Language::read_id_from_code(conn, "pl").unwrap(); - let comment_form_4 = CommentForm { - content: "Comment 4".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - language_id: Some(polish_id), - ..CommentForm::default() - }; + let comment_form_4 = CommentInsertForm::builder() + .content("Comment 4".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .language_id(Some(polish_id)) + .build(); let inserted_comment_4 = Comment::create(conn, &comment_form_4, Some(&inserted_comment_1.path)).unwrap(); - let comment_form_5 = CommentForm { - content: "Comment 5".into(), - creator_id: inserted_person.id, - post_id: inserted_post.id, - ..CommentForm::default() - }; + let comment_form_5 = CommentInsertForm::builder() + .content("Comment 5".into()) + .creator_id(inserted_person.id) + .post_id(inserted_post.id) + .build(); let _inserted_comment_5 = Comment::create(conn, &comment_form_5, Some(&inserted_comment_4.path)).unwrap(); @@ -554,6 +550,7 @@ mod tests { let _inserted_comment_like = CommentLike::like(conn, &comment_like_form).unwrap(); Data { + inserted_instance, inserted_comment_0, inserted_comment_1, inserted_comment_2, @@ -743,6 +740,7 @@ mod tests { Community::delete(conn, data.inserted_community.id).unwrap(); Person::delete(conn, data.inserted_person.id).unwrap(); Person::delete(conn, data.inserted_person_2.id).unwrap(); + Instance::delete(conn, data.inserted_instance.id).unwrap(); } fn expected_comment_view(data: &Data, conn: &mut PgConnection) -> CommentView { @@ -787,6 +785,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: data.inserted_instance.id, }, post: Post { id: data.inserted_post.id, @@ -826,6 +825,7 @@ mod tests { hidden: false, posting_restricted_to_mods: false, published: data.inserted_community.published, + instance_id: data.inserted_instance.id, }, counts: CommentAggregates { id: agg.id, diff --git a/crates/db_views/src/post_report_view.rs b/crates/db_views/src/post_report_view.rs index 472795b5..83c43ff4 100644 --- a/crates/db_views/src/post_report_view.rs +++ b/crates/db_views/src/post_report_view.rs @@ -272,6 +272,7 @@ mod tests { aggregates::structs::PostAggregates, source::{ community::*, + instance::Instance, person::*, post::*, post_report::{PostReport, PostReportForm}, @@ -286,37 +287,39 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person = PersonForm { - name: "timmy_prv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person = PersonInsertForm::builder() + .name("timmy_prv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_timmy = Person::create(conn, &new_person).unwrap(); - let new_person_2 = PersonForm { - name: "sara_prv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_2 = PersonInsertForm::builder() + .name("sara_prv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_sara = Person::create(conn, &new_person_2).unwrap(); // Add a third person, since new ppl can only report something once. - let new_person_3 = PersonForm { - name: "jessica_prv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_3 = PersonInsertForm::builder() + .name("jessica_prv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_jessica = Person::create(conn, &new_person_3).unwrap(); - let new_community = CommunityForm { - name: "test community prv".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test community prv".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); @@ -328,12 +331,11 @@ mod tests { let _inserted_moderator = CommunityModerator::join(conn, &timmy_moderator_form).unwrap(); - let new_post = PostForm { - name: "A test post crv".into(), - creator_id: inserted_timmy.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("A test post crv".into()) + .creator_id(inserted_timmy.id) + .community_id(inserted_community.id) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); @@ -384,6 +386,7 @@ mod tests { hidden: false, posting_restricted_to_mods: false, published: inserted_community.published, + instance_id: inserted_instance.id, }, creator: PersonSafe { id: inserted_jessica.id, @@ -404,6 +407,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }, post_creator: PersonSafe { id: inserted_timmy.id, @@ -424,6 +428,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }, creator_banned_from_community: false, my_vote: None, @@ -466,6 +471,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }; // Do a batch read of timmys reports @@ -524,6 +530,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: inserted_instance.id, }); assert_eq!( @@ -551,5 +558,6 @@ mod tests { Person::delete(conn, inserted_sara.id).unwrap(); Person::delete(conn, inserted_jessica.id).unwrap(); Community::delete(conn, inserted_community.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_views/src/post_view.rs b/crates/db_views/src/post_view.rs index 9db1d095..aebddc1c 100644 --- a/crates/db_views/src/post_view.rs +++ b/crates/db_views/src/post_view.rs @@ -457,8 +457,9 @@ mod tests { actor_language::LocalUserLanguage, community::*, community_block::{CommunityBlock, CommunityBlockForm}, + instance::Instance, language::Language, - local_user::{LocalUser, LocalUserForm}, + local_user::{LocalUser, LocalUserInsertForm, LocalUserUpdateForm}, person::*, person_block::{PersonBlock, PersonBlockForm}, post::*, @@ -471,6 +472,7 @@ mod tests { use serial_test::serial; struct Data { + inserted_instance: Instance, inserted_person: Person, inserted_local_user: LocalUser, inserted_blocked_person: Person, @@ -480,57 +482,57 @@ mod tests { } fn init_data(conn: &mut PgConnection) -> Data { + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + let person_name = "tegan".to_string(); - let new_person = PersonForm { - name: person_name.to_owned(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person = PersonInsertForm::builder() + .name(person_name.to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_person = Person::create(conn, &new_person).unwrap(); - let local_user_form = LocalUserForm { - person_id: Some(inserted_person.id), - password_encrypted: Some("".to_string()), - ..Default::default() - }; + let local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_person.id) + .password_encrypted("".to_string()) + .build(); let inserted_local_user = LocalUser::create(conn, &local_user_form).unwrap(); - let new_bot = PersonForm { - name: "mybot".to_string(), - bot_account: Some(true), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_bot = PersonInsertForm::builder() + .name("mybot".to_string()) + .bot_account(Some(true)) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_bot = Person::create(conn, &new_bot).unwrap(); - let new_community = CommunityForm { - name: "test_community_3".to_string(), - title: "nada".to_owned(), - public_key: Some("pubkey".to_string()), - ..CommunityForm::default() - }; + let new_community = CommunityInsertForm::builder() + .name("test_community_3".to_string()) + .title("nada".to_owned()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_community = Community::create(conn, &new_community).unwrap(); // Test a person block, make sure the post query doesn't include their post - let blocked_person = PersonForm { - name: person_name, - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let blocked_person = PersonInsertForm::builder() + .name(person_name) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_blocked_person = Person::create(conn, &blocked_person).unwrap(); - let post_from_blocked_person = PostForm { - name: "blocked_person_post".to_string(), - creator_id: inserted_blocked_person.id, - community_id: inserted_community.id, - language_id: Some(LanguageId(1)), - ..PostForm::default() - }; + let post_from_blocked_person = PostInsertForm::builder() + .name("blocked_person_post".to_string()) + .creator_id(inserted_blocked_person.id) + .community_id(inserted_community.id) + .language_id(Some(LanguageId(1))) + .build(); Post::create(conn, &post_from_blocked_person).unwrap(); @@ -543,26 +545,25 @@ mod tests { PersonBlock::block(conn, &person_block).unwrap(); // A sample post - let new_post = PostForm { - name: "test post 3".to_string(), - creator_id: inserted_person.id, - community_id: inserted_community.id, - language_id: Some(LanguageId(47)), - ..PostForm::default() - }; + let new_post = PostInsertForm::builder() + .name("test post 3".to_string()) + .creator_id(inserted_person.id) + .community_id(inserted_community.id) + .language_id(Some(LanguageId(47))) + .build(); let inserted_post = Post::create(conn, &new_post).unwrap(); - let new_bot_post = PostForm { - name: "test bot post".to_string(), - creator_id: inserted_bot.id, - community_id: inserted_community.id, - ..PostForm::default() - }; + let new_bot_post = PostInsertForm::builder() + .name("test bot post".to_string()) + .creator_id(inserted_bot.id) + .community_id(inserted_community.id) + .build(); let _inserted_bot_post = Post::create(conn, &new_bot_post).unwrap(); Data { + inserted_instance, inserted_person, inserted_local_user, inserted_blocked_person, @@ -578,10 +579,9 @@ mod tests { let conn = &mut establish_unpooled_connection(); let data = init_data(conn); - let local_user_form = LocalUserForm { - show_bot_accounts: Some(false), - ..Default::default() - }; + let local_user_form = LocalUserUpdateForm::builder() + .show_bot_accounts(Some(false)) + .build(); let inserted_local_user = LocalUser::update(conn, data.inserted_local_user.id, &local_user_form).unwrap(); @@ -609,10 +609,9 @@ mod tests { post_listing_single_with_person ); - let local_user_form = LocalUserForm { - show_bot_accounts: Some(true), - ..Default::default() - }; + let local_user_form = LocalUserUpdateForm::builder() + .show_bot_accounts(Some(true)) + .build(); let inserted_local_user = LocalUser::update(conn, data.inserted_local_user.id, &local_user_form).unwrap(); @@ -727,13 +726,12 @@ mod tests { let data = init_data(conn); let spanish_id = Language::read_id_from_code(conn, "es").unwrap(); - let post_spanish = PostForm { - name: "asffgdsc".to_string(), - creator_id: data.inserted_person.id, - community_id: data.inserted_community.id, - language_id: Some(spanish_id), - ..PostForm::default() - }; + let post_spanish = PostInsertForm::builder() + .name("asffgdsc".to_string()) + .creator_id(data.inserted_person.id) + .community_id(data.inserted_community.id) + .language_id(Some(spanish_id)) + .build(); Post::create(conn, &post_spanish).unwrap(); @@ -795,6 +793,7 @@ mod tests { Person::delete(conn, data.inserted_person.id).unwrap(); Person::delete(conn, data.inserted_bot.id).unwrap(); Person::delete(conn, data.inserted_blocked_person.id).unwrap(); + Instance::delete(conn, data.inserted_instance.id).unwrap(); assert_eq!(1, num_deleted); } @@ -850,6 +849,7 @@ mod tests { shared_inbox_url: None, matrix_user_id: None, ban_expires: None, + instance_id: data.inserted_instance.id, }, creator_banned_from_community: false, community: CommunitySafe { @@ -868,6 +868,7 @@ mod tests { hidden: false, posting_restricted_to_mods: false, published: inserted_community.published, + instance_id: data.inserted_instance.id, }, counts: PostAggregates { id: agg.id, diff --git a/crates/db_views/src/private_message_report_view.rs b/crates/db_views/src/private_message_report_view.rs index 9a597684..2b198196 100644 --- a/crates/db_views/src/private_message_report_view.rs +++ b/crates/db_views/src/private_message_report_view.rs @@ -148,8 +148,9 @@ mod tests { use crate::private_message_report_view::PrivateMessageReportQuery; use lemmy_db_schema::{ source::{ - person::{Person, PersonForm}, - private_message::{PrivateMessage, PrivateMessageForm}, + instance::Instance, + person::{Person, PersonInsertForm}, + private_message::{PrivateMessage, PrivateMessageInsertForm}, private_message_report::{PrivateMessageReport, PrivateMessageReportForm}, }, traits::{Crud, Reportable}, @@ -162,27 +163,28 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let new_person_1 = PersonForm { - name: "timmy_mrv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let new_person_1 = PersonInsertForm::builder() + .name("timmy_mrv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_timmy = Person::create(conn, &new_person_1).unwrap(); - let new_person_2 = PersonForm { - name: "jessica_mrv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_2 = PersonInsertForm::builder() + .name("jessica_mrv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_jessica = Person::create(conn, &new_person_2).unwrap(); // timmy sends private message to jessica - let pm_form = PrivateMessageForm { - creator_id: inserted_timmy.id, - recipient_id: inserted_jessica.id, - content: "something offensive".to_string(), - ..Default::default() - }; + let pm_form = PrivateMessageInsertForm::builder() + .creator_id(inserted_timmy.id) + .recipient_id(inserted_jessica.id) + .content("something offensive".to_string()) + .build(); let pm = PrivateMessage::create(conn, &pm_form).unwrap(); // jessica reports private message @@ -206,11 +208,11 @@ mod tests { assert_eq!(pm_report.reason, reports[0].private_message_report.reason); assert_eq!(pm.content, reports[0].private_message.content); - let new_person_3 = PersonForm { - name: "admin_mrv".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let new_person_3 = PersonInsertForm::builder() + .name("admin_mrv".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_admin = Person::create(conn, &new_person_3).unwrap(); // admin resolves the report (after taking appropriate action) @@ -229,5 +231,7 @@ mod tests { inserted_admin.name, reports[0].resolver.as_ref().unwrap().name ); + + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_views/src/registration_application_view.rs b/crates/db_views/src/registration_application_view.rs index b4c84829..265292ef 100644 --- a/crates/db_views/src/registration_application_view.rs +++ b/crates/db_views/src/registration_application_view.rs @@ -157,9 +157,14 @@ mod tests { }; use lemmy_db_schema::{ source::{ - local_user::{LocalUser, LocalUserForm, LocalUserSettings}, + instance::Instance, + local_user::{LocalUser, LocalUserInsertForm, LocalUserSettings, LocalUserUpdateForm}, person::*, - registration_application::{RegistrationApplication, RegistrationApplicationForm}, + registration_application::{ + RegistrationApplication, + RegistrationApplicationInsertForm, + RegistrationApplicationUpdateForm, + }, }, traits::Crud, utils::establish_unpooled_connection, @@ -171,71 +176,68 @@ mod tests { fn test_crud() { let conn = &mut establish_unpooled_connection(); - let timmy_person_form = PersonForm { - name: "timmy_rav".into(), - admin: Some(true), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let inserted_instance = Instance::create(conn, "my_domain.tld").unwrap(); + + let timmy_person_form = PersonInsertForm::builder() + .name("timmy_rav".into()) + .admin(Some(true)) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_timmy_person = Person::create(conn, &timmy_person_form).unwrap(); - let timmy_local_user_form = LocalUserForm { - person_id: Some(inserted_timmy_person.id), - password_encrypted: Some("nada".to_string()), - ..LocalUserForm::default() - }; + let timmy_local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_timmy_person.id) + .password_encrypted("nada".to_string()) + .build(); let _inserted_timmy_local_user = LocalUser::create(conn, &timmy_local_user_form).unwrap(); - let sara_person_form = PersonForm { - name: "sara_rav".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let sara_person_form = PersonInsertForm::builder() + .name("sara_rav".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_sara_person = Person::create(conn, &sara_person_form).unwrap(); - let sara_local_user_form = LocalUserForm { - person_id: Some(inserted_sara_person.id), - password_encrypted: Some("nada".to_string()), - ..LocalUserForm::default() - }; + let sara_local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_sara_person.id) + .password_encrypted("nada".to_string()) + .build(); let inserted_sara_local_user = LocalUser::create(conn, &sara_local_user_form).unwrap(); // Sara creates an application - let sara_app_form = RegistrationApplicationForm { - local_user_id: Some(inserted_sara_local_user.id), - answer: Some("LET ME IIIIINN".to_string()), - ..RegistrationApplicationForm::default() + let sara_app_form = RegistrationApplicationInsertForm { + local_user_id: inserted_sara_local_user.id, + answer: "LET ME IIIIINN".to_string(), }; let sara_app = RegistrationApplication::create(conn, &sara_app_form).unwrap(); let read_sara_app_view = RegistrationApplicationView::read(conn, sara_app.id).unwrap(); - let jess_person_form = PersonForm { - name: "jess_rav".into(), - public_key: Some("pubkey".to_string()), - ..PersonForm::default() - }; + let jess_person_form = PersonInsertForm::builder() + .name("jess_rav".into()) + .public_key("pubkey".to_string()) + .instance_id(inserted_instance.id) + .build(); let inserted_jess_person = Person::create(conn, &jess_person_form).unwrap(); - let jess_local_user_form = LocalUserForm { - person_id: Some(inserted_jess_person.id), - password_encrypted: Some("nada".to_string()), - ..LocalUserForm::default() - }; + let jess_local_user_form = LocalUserInsertForm::builder() + .person_id(inserted_jess_person.id) + .password_encrypted("nada".to_string()) + .build(); let inserted_jess_local_user = LocalUser::create(conn, &jess_local_user_form).unwrap(); // Sara creates an application - let jess_app_form = RegistrationApplicationForm { - local_user_id: Some(inserted_jess_local_user.id), - answer: Some("LET ME IIIIINN".to_string()), - ..RegistrationApplicationForm::default() + let jess_app_form = RegistrationApplicationInsertForm { + local_user_id: inserted_jess_local_user.id, + answer: "LET ME IIIIINN".to_string(), }; let jess_app = RegistrationApplication::create(conn, &jess_app_form).unwrap(); @@ -282,6 +284,7 @@ mod tests { inbox_url: inserted_sara_person.inbox_url.to_owned(), shared_inbox_url: None, matrix_user_id: None, + instance_id: inserted_instance.id, }, admin: None, }; @@ -309,19 +312,17 @@ mod tests { assert_eq!(unread_count, 2); // Approve the application - let approve_form = RegistrationApplicationForm { - admin_id: Some(inserted_timmy_person.id), + let approve_form = RegistrationApplicationUpdateForm { + admin_id: Some(Some(inserted_timmy_person.id)), deny_reason: None, - ..RegistrationApplicationForm::default() }; RegistrationApplication::update(conn, sara_app.id, &approve_form).unwrap(); // Update the local_user row - let approve_local_user_form = LocalUserForm { - accepted_application: Some(true), - ..LocalUserForm::default() - }; + let approve_local_user_form = LocalUserUpdateForm::builder() + .accepted_application(Some(true)) + .build(); LocalUser::update(conn, inserted_sara_local_user.id, &approve_local_user_form).unwrap(); @@ -353,6 +354,7 @@ mod tests { inbox_url: inserted_timmy_person.inbox_url.to_owned(), shared_inbox_url: None, matrix_user_id: None, + instance_id: inserted_instance.id, }); assert_eq!(read_sara_app_view_after_approve, expected_sara_app_view); @@ -382,5 +384,6 @@ mod tests { Person::delete(conn, inserted_timmy_person.id).unwrap(); Person::delete(conn, inserted_sara_person.id).unwrap(); Person::delete(conn, inserted_jess_person.id).unwrap(); + Instance::delete(conn, inserted_instance.id).unwrap(); } } diff --git a/crates/db_views/src/site_view.rs b/crates/db_views/src/site_view.rs index 03ee0d7d..10b36ac8 100644 --- a/crates/db_views/src/site_view.rs +++ b/crates/db_views/src/site_view.rs @@ -2,19 +2,32 @@ use crate::structs::SiteView; use diesel::{result::Error, *}; use lemmy_db_schema::{ aggregates::structs::SiteAggregates, - schema::{site, site_aggregates}, - source::site::Site, + schema::{local_site, local_site_rate_limit, site, site_aggregates}, + source::{local_site::LocalSite, local_site_rate_limit::LocalSiteRateLimit, site::Site}, }; impl SiteView { pub fn read_local(conn: &mut PgConnection) -> Result { - let (mut site, counts) = site::table + let (mut site, local_site, local_site_rate_limit, counts) = site::table + .inner_join(local_site::table) + .inner_join( + local_site_rate_limit::table.on(local_site::id.eq(local_site_rate_limit::local_site_id)), + ) .inner_join(site_aggregates::table) - .select((site::all_columns, site_aggregates::all_columns)) - .order_by(site::id) - .first::<(Site, SiteAggregates)>(conn)?; + .select(( + site::all_columns, + local_site::all_columns, + local_site_rate_limit::all_columns, + site_aggregates::all_columns, + )) + .first::<(Site, LocalSite, LocalSiteRateLimit, SiteAggregates)>(conn)?; site.private_key = None; - Ok(SiteView { site, counts }) + Ok(SiteView { + site, + local_site, + local_site_rate_limit, + counts, + }) } } diff --git a/crates/db_views/src/structs.rs b/crates/db_views/src/structs.rs index 5b068d9a..1e4fe70a 100644 --- a/crates/db_views/src/structs.rs +++ b/crates/db_views/src/structs.rs @@ -5,6 +5,8 @@ use lemmy_db_schema::{ comment_report::CommentReport, community::CommunitySafe, language::Language, + local_site::LocalSite, + local_site_rate_limit::LocalSiteRateLimit, local_user::{LocalUser, LocalUserSettings}, person::{Person, PersonSafe}, post::Post, @@ -115,6 +117,8 @@ pub struct RegistrationApplicationView { #[derive(Debug, Serialize, Deserialize, Clone)] pub struct SiteView { pub site: Site, + pub local_site: LocalSite, + pub local_site_rate_limit: LocalSiteRateLimit, pub counts: SiteAggregates, } diff --git a/crates/routes/src/nodeinfo.rs b/crates/routes/src/nodeinfo.rs index 1841b159..c0d7cf7e 100644 --- a/crates/routes/src/nodeinfo.rs +++ b/crates/routes/src/nodeinfo.rs @@ -33,7 +33,7 @@ async fn node_info(context: web::Data) -> Result) -> Result Option { - self.slur_filter.as_ref().map(|slurs| { - RegexBuilder::new(slurs) - .case_insensitive(true) - .build() - .expect("compile regex") - }) - } - pub fn pictrs_config(&self) -> Result { self .pictrs diff --git a/crates/utils/src/settings/structs.rs b/crates/utils/src/settings/structs.rs index 28dcb5c5..d32f3dc2 100644 --- a/crates/utils/src/settings/structs.rs +++ b/crates/utils/src/settings/structs.rs @@ -9,17 +9,10 @@ pub struct Settings { /// settings related to the postgresql database #[default(Default::default())] pub database: DatabaseConfig, - /// rate limits for various user actions, by user ip - #[default(Some(Default::default()))] - pub rate_limit: Option, /// Settings related to activitypub federation - #[default(Default::default())] - pub federation: FederationConfig, /// Pictrs image server configuration. #[default(Some(Default::default()))] pub(crate) pictrs: Option, - #[default(Default::default())] - pub captcha: CaptchaConfig, /// Email sending configuration. All options except login/password are mandatory #[default(None)] #[doku(example = "Some(Default::default())")] @@ -42,14 +35,6 @@ pub struct Settings { /// Whether the site is available over TLS. Needs to be true for federation to work. #[default(true)] pub tls_enabled: bool, - #[default(None)] - #[doku(example = "(\\bThis\\b)|(\\bis\\b)|(\\bsample\\b)")] - /// A regex list of slurs to block / hide - pub slur_filter: Option, - /// Maximum length of local community and user names - #[default(20)] - pub actor_name_max_length: usize, - /// Set the URL for opentelemetry exports. If you do not have an opentelemetry collector, do not set this option #[default(None)] #[doku(skip)] @@ -69,17 +54,6 @@ pub struct PictrsConfig { pub api_key: Option, } -#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] -#[serde(default)] -pub struct CaptchaConfig { - /// Whether captcha is required for signup - #[default(false)] - pub enabled: bool, - /// Can be easy, medium, or hard - #[default("medium")] - pub difficulty: String, -} - #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] #[serde(default)] pub struct DatabaseConfig { @@ -121,82 +95,6 @@ pub struct EmailConfig { pub tls_type: String, } -#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] -#[serde(default)] -pub struct FederationConfig { - /// Whether to enable activitypub federation. - #[default(false)] - pub enabled: bool, - /// 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 - #[default(None)] - #[doku(example = "instance1.tld")] - #[doku(example = "instance2.tld")] - pub allowed_instances: Option>, - /// Instances which we never federate anything with (but previously federated objects are unaffected) - #[default(None)] - pub blocked_instances: Option>, - /// 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). - #[default(true)] - pub strict_allowlist: bool, - /// Maximum number of HTTP requests allowed to handle a single incoming activity (or a single object fetch through the search). - #[default(25)] - pub http_fetch_retry_limit: i32, - /// 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. - #[default(64)] - pub worker_count: u64, - /// Use federation debug mode. Allows connecting to http and localhost urls. Also sends outgoing - /// activities synchronously for easier testing. Do not use in production. - #[default(false)] - pub debug: bool, -} - -#[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] -#[serde(default)] -pub struct RateLimitConfig { - /// Maximum number of messages created in interval - #[default(180)] - pub message: i32, - /// Interval length for message limit, in seconds - #[default(60)] - pub message_per_second: i32, - /// Maximum number of posts created in interval - #[default(6)] - pub post: i32, - /// Interval length for post limit, in seconds - #[default(600)] - pub post_per_second: i32, - /// Maximum number of registrations in interval - #[default(3)] - pub register: i32, - /// Interval length for registration limit, in seconds - #[default(3600)] - pub register_per_second: i32, - /// Maximum number of image uploads in interval - #[default(6)] - pub image: i32, - /// Interval length for image uploads, in seconds - #[default(3600)] - pub image_per_second: i32, - /// Maximum number of comments created in interval - #[default(6)] - pub comment: i32, - /// Interval length for comment limit, in seconds - #[default(600)] - pub comment_per_second: i32, - #[default(60)] - pub search: i32, - /// Interval length for search limit, in seconds - #[default(600)] - pub search_per_second: i32, -} - #[derive(Debug, Deserialize, Serialize, Clone, SmartDefault, Document)] pub struct SetupConfig { /// Username for the admin user diff --git a/crates/utils/src/test.rs b/crates/utils/src/test.rs index e887bae4..e7bfdb59 100644 --- a/crates/utils/src/test.rs +++ b/crates/utils/src/test.rs @@ -1,15 +1,14 @@ -use crate::{ - settings::SETTINGS, - utils::{ - is_valid_actor_name, - is_valid_display_name, - is_valid_matrix_id, - is_valid_post_title, - remove_slurs, - scrape_text_for_mentions, - slur_check, - slurs_vec_to_str, - }, +use regex::RegexBuilder; + +use crate::utils::{ + is_valid_actor_name, + is_valid_display_name, + is_valid_matrix_id, + is_valid_post_title, + remove_slurs, + scrape_text_for_mentions, + slur_check, + slurs_vec_to_str, }; #[test] @@ -24,7 +23,7 @@ fn test_mentions_regex() { #[test] fn test_valid_actor_name() { - let actor_name_max_length = SETTINGS.actor_name_max_length; + let actor_name_max_length = 20; assert!(is_valid_actor_name("Hello_98", actor_name_max_length)); assert!(is_valid_actor_name("ten", actor_name_max_length)); assert!(!is_valid_actor_name("Hello-98", actor_name_max_length)); @@ -34,7 +33,7 @@ fn test_valid_actor_name() { #[test] fn test_valid_display_name() { - let actor_name_max_length = SETTINGS.actor_name_max_length; + let actor_name_max_length = 20; assert!(is_valid_display_name("hello @there", actor_name_max_length)); assert!(!is_valid_display_name( "@hello there", @@ -65,7 +64,7 @@ fn test_valid_matrix_id() { #[test] fn test_slur_filter() { - let slur_regex = SETTINGS.slur_regex(); + let slur_regex = Some(RegexBuilder::new(r"(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?)").case_insensitive(true).build().unwrap()); let test = "faggot test kike tranny cocksucker retardeds. Capitalized Niggerz. This is a bunch of other safe text."; let slur_free = "No slurs here"; diff --git a/crates/utils/src/utils.rs b/crates/utils/src/utils.rs index 99dd21c8..9d481de1 100644 --- a/crates/utils/src/utils.rs +++ b/crates/utils/src/utils.rs @@ -1,10 +1,11 @@ -use crate::{error::LemmyError, IpAddr}; +use crate::{error::LemmyError, location_info, IpAddr}; use actix_web::dev::ConnectionInfo; +use anyhow::Context; use chrono::{DateTime, FixedOffset, NaiveDateTime}; use itertools::Itertools; use once_cell::sync::Lazy; use rand::{distributions::Alphanumeric, thread_rng, Rng}; -use regex::Regex; +use regex::{Regex, RegexBuilder}; use url::Url; static MENTIONS_REGEX: Lazy = Lazy::new(|| { @@ -60,6 +61,15 @@ pub(crate) fn slur_check<'a>( } } +pub fn build_slur_regex(regex_str: Option<&str>) -> Option { + regex_str.map(|slurs| { + RegexBuilder::new(slurs) + .case_insensitive(true) + .build() + .expect("compile regex") + }) +} + pub fn check_slurs(text: &str, slur_regex: &Option) -> Result<(), LemmyError> { if let Err(slurs) = slur_check(text, slur_regex) { Err(LemmyError::from_error_message( @@ -87,6 +97,20 @@ pub(crate) fn slurs_vec_to_str(slurs: Vec<&str>) -> String { [start, combined].concat() } +/// Make sure if applications are required, that there is an application questionnaire +pub fn check_application_question( + application_question: &Option>, + require_application: &Option, +) -> Result<(), LemmyError> { + if require_application.unwrap_or(false) + && application_question.as_ref().unwrap_or(&None).is_none() + { + Err(LemmyError::from_message("application_question_required")) + } else { + Ok(()) + } +} + pub fn generate_random_string() -> String { thread_rng() .sample_iter(&Alphanumeric) @@ -178,6 +202,10 @@ pub fn clean_url_params(url: &Url) -> Url { url_out } +pub fn generate_domain_url(actor_id: &Url) -> Result { + Ok(actor_id.host_str().context(location_info!())?.to_string()) +} + #[cfg(test)] mod tests { use crate::utils::{clean_url_params, is_valid_post_title}; diff --git a/crates/utils/translations b/crates/utils/translations index f5d6f0ea..454debae 160000 --- a/crates/utils/translations +++ b/crates/utils/translations @@ -1 +1 @@ -Subproject commit f5d6f0eabafd559417bf8f203fd655f7858bffcf +Subproject commit 454debaede4cc932ac15fea9bf620cf1daf1ae4c diff --git a/crates/websocket/src/send.rs b/crates/websocket/src/send.rs index ce0f7399..59187d2c 100644 --- a/crates/websocket/src/send.rs +++ b/crates/websocket/src/send.rs @@ -14,9 +14,9 @@ use lemmy_db_schema::{ newtypes::{CommentId, CommunityId, LocalUserId, PersonId, PostId, PrivateMessageId}, source::{ comment::Comment, - comment_reply::{CommentReply, CommentReplyForm}, + comment_reply::{CommentReply, CommentReplyInsertForm}, person::Person, - person_mention::{PersonMention, PersonMentionForm}, + person_mention::{PersonMention, PersonMentionInsertForm}, post::Post, }, traits::{Crud, DeleteableOrRemoveable}, @@ -197,7 +197,7 @@ pub async fn send_local_notifs( // This can cause two notifications, one for reply and the other for mention recipient_ids.push(mention_user_view.local_user.id); - let user_mention_form = PersonMentionForm { + let user_mention_form = PersonMentionInsertForm { recipient_id: mention_user_view.person.id, comment_id: comment.id, read: None, @@ -248,7 +248,7 @@ pub async fn send_local_notifs( if let Ok(parent_user_view) = user_view { recipient_ids.push(parent_user_view.local_user.id); - let comment_reply_form = CommentReplyForm { + let comment_reply_form = CommentReplyInsertForm { recipient_id: parent_user_view.person.id, comment_id: comment.id, read: None, @@ -289,7 +289,7 @@ pub async fn send_local_notifs( if let Ok(parent_user_view) = parent_user { recipient_ids.push(parent_user_view.local_user.id); - let comment_reply_form = CommentReplyForm { + let comment_reply_form = CommentReplyInsertForm { recipient_id: parent_user_view.person.id, comment_id: comment.id, read: None, diff --git a/docker/federation/lemmy_alpha.hjson b/docker/federation/lemmy_alpha.hjson index 246c1f5c..9622ead4 100644 --- a/docker/federation/lemmy_alpha.hjson +++ b/docker/federation/lemmy_alpha.hjson @@ -6,7 +6,6 @@ admin_username: lemmy_alpha admin_password: lemmylemmy site_name: lemmy-alpha - sidebar: alphas sidebar } database: { database: lemmy @@ -16,25 +15,4 @@ port: 5432 pool_size: 5 } - federation: { - enabled: true - allowed_instances: ["lemmy-beta","lemmy-gamma","lemmy-delta","lemmy-epsilon"] - debug: true - } - captcha: { - enabled: false - difficulty: medium - } - rate_limit: { - message: 180 - message_per_second: 60 - post: 99999 - post_per_second: 600 - register: 99999 - register_per_second: 3600 - image: 6 - image_per_second: 3600 - comment: 99999 - comment_per_second: 600 - } } diff --git a/docker/federation/lemmy_beta.hjson b/docker/federation/lemmy_beta.hjson index 87184f1a..f199055d 100644 --- a/docker/federation/lemmy_beta.hjson +++ b/docker/federation/lemmy_beta.hjson @@ -15,25 +15,4 @@ port: 5432 pool_size: 5 } - federation: { - enabled: true - allowed_instances: ["lemmy-alpha","lemmy-gamma","lemmy-delta","lemmy-epsilon"] - debug: true - } - captcha: { - enabled: false - difficulty: medium - } - rate_limit: { - message: 180 - message_per_second: 60 - post: 99999 - post_per_second: 600 - register: 99999 - register_per_second: 3600 - image: 6 - image_per_second: 3600 - comment: 99999 - comment_per_second: 600 - } } diff --git a/docker/federation/lemmy_delta.hjson b/docker/federation/lemmy_delta.hjson index 9c1c7aee..98f7606f 100644 --- a/docker/federation/lemmy_delta.hjson +++ b/docker/federation/lemmy_delta.hjson @@ -15,25 +15,4 @@ port: 5432 pool_size: 5 } - federation: { - enabled: true - allowed_instances: ["lemmy-beta"] - debug: true - } - captcha: { - enabled: false - difficulty: medium - } - rate_limit: { - message: 180 - message_per_second: 60 - post: 99999 - post_per_second: 600 - register: 99999 - register_per_second: 3600 - image: 6 - image_per_second: 3600 - comment: 99999 - comment_per_second: 600 - } } diff --git a/docker/federation/lemmy_epsilon.hjson b/docker/federation/lemmy_epsilon.hjson index c1b19f07..f7c64e2f 100644 --- a/docker/federation/lemmy_epsilon.hjson +++ b/docker/federation/lemmy_epsilon.hjson @@ -15,25 +15,4 @@ port: 5432 pool_size: 5 } - federation: { - enabled: true - blocked_instances: ["lemmy-alpha"] - debug: true - } - captcha: { - enabled: false - difficulty: medium - } - rate_limit: { - message: 180 - message_per_second: 60 - post: 99999 - post_per_second: 600 - register: 99999 - register_per_second: 3600 - image: 6 - image_per_second: 3600 - comment: 99999 - comment_per_second: 600 - } } diff --git a/docker/federation/lemmy_gamma.hjson b/docker/federation/lemmy_gamma.hjson index 4d479174..632f10df 100644 --- a/docker/federation/lemmy_gamma.hjson +++ b/docker/federation/lemmy_gamma.hjson @@ -15,25 +15,4 @@ port: 5432 pool_size: 5 } - federation: { - enabled: true - allowed_instances: ["lemmy-alpha","lemmy-beta","lemmy-delta","lemmy-epsilon"] - debug: true - } - captcha: { - enabled: false - difficulty: medium - } - rate_limit: { - message: 180 - message_per_second: 60 - post: 99999 - post_per_second: 600 - register: 99999 - register_per_second: 3600 - image: 6 - image_per_second: 3600 - comment: 99999 - comment_per_second: 600 - } } diff --git a/migrations/2022-10-06-183632_move_blocklist_to_db/down.sql b/migrations/2022-10-06-183632_move_blocklist_to_db/down.sql new file mode 100644 index 00000000..3ca49b54 --- /dev/null +++ b/migrations/2022-10-06-183632_move_blocklist_to_db/down.sql @@ -0,0 +1,63 @@ +-- Add back site columns +alter table site + add column enable_downvotes boolean default true not null, + add column open_registration boolean default true not null, + add column enable_nsfw boolean default true not null, + add column community_creation_admin_only boolean default false not null, + add column require_email_verification boolean default false not null, + add column require_application boolean default true not null, + add column application_question text default 'to verify that you are human, please explain why you want to create an account on this site'::text, + add column private_instance boolean default false not null, + add column default_theme text default 'browser'::text not null, + add column default_post_listing_type text default 'Local'::text not null, + add column legal_information text, + add column hide_modlog_mod_names boolean default true not null, + add column application_email_admins boolean default false not null; + +-- Insert the data back from local_site +update site set + enable_downvotes = ls.enable_downvotes, + open_registration = ls.open_registration, + enable_nsfw = ls.enable_nsfw, + community_creation_admin_only = ls.community_creation_admin_only, + require_email_verification = ls.require_email_verification, + require_application = ls.require_application, + application_question = ls.application_question, + private_instance = ls.private_instance, + default_theme = ls.default_theme, + default_post_listing_type = ls.default_post_listing_type, + legal_information = ls.legal_information, + hide_modlog_mod_names = ls.hide_modlog_mod_names, + application_email_admins = ls.application_email_admins, + published = ls.published, + updated = ls.updated +from (select + site_id, + enable_downvotes, + open_registration, + enable_nsfw, + community_creation_admin_only, + require_email_verification, + require_application, + application_question, + private_instance, + default_theme, + default_post_listing_type, + legal_information, + hide_modlog_mod_names, + application_email_admins, + published, + updated +from local_site) as ls +where site.id = ls.site_id; + +-- drop instance columns +alter table site drop column instance_id; +alter table person drop column instance_id; +alter table community drop column instance_id; + +drop table local_site_rate_limit; +drop table local_site; +drop table federation_allowlist; +drop table federation_blocklist; +drop table instance; diff --git a/migrations/2022-10-06-183632_move_blocklist_to_db/up.sql b/migrations/2022-10-06-183632_move_blocklist_to_db/up.sql new file mode 100644 index 00000000..e814ce27 --- /dev/null +++ b/migrations/2022-10-06-183632_move_blocklist_to_db/up.sql @@ -0,0 +1,186 @@ +-- Create an instance table +-- Holds any connected or unconnected domain +create table instance ( + id serial primary key, + domain varchar(255) not null unique, + published timestamp not null default now(), + updated timestamp null +); + +-- Insert all the domains to the instance table +insert into instance (domain) +select distinct substring(p.actor_id from '(?:.*://)?(?:www\.)?([^/?]*)') from ( + select actor_id from site + union + select actor_id from person + union + select actor_id from community +) as p; + +-- Alter site, person, and community tables to reference the instance table. +alter table site add column +instance_id int references instance on update cascade on delete cascade; + +alter table person add column +instance_id int references instance on update cascade on delete cascade; + +alter table community add column +instance_id int references instance on update cascade on delete cascade; + +-- Add those columns +update site set instance_id = i.id +from instance i +where substring(actor_id from '(?:.*://)?(?:www\.)?([^/?]*)') = i.domain; + +update person set instance_id = i.id +from instance i +where substring(actor_id from '(?:.*://)?(?:www\.)?([^/?]*)') = i.domain; + +update community set instance_id = i.id +from instance i +where substring(actor_id from '(?:.*://)?(?:www\.)?([^/?]*)') = i.domain; + +-- Make those columns unique not null now +alter table site alter column instance_id set not null; +alter table site add constraint idx_site_instance_unique unique (instance_id); + +alter table person alter column instance_id set not null; +alter table community alter column instance_id set not null; + +-- Create allowlist and blocklist tables +create table federation_allowlist ( + id serial primary key, + instance_id int references instance on update cascade on delete cascade not null unique, + published timestamp not null default now(), + updated timestamp null +); + +create table federation_blocklist ( + id serial primary key, + instance_id int references instance on update cascade on delete cascade not null unique, + published timestamp not null default now(), + updated timestamp null +); + +-- Move all the extra site settings-type columns to a local_site table +-- Add a lot of other fields currently in the lemmy.hjson +create table local_site ( + id serial primary key, + site_id int references site on update cascade on delete cascade not null unique, + + -- Site table fields + site_setup boolean default false not null, + enable_downvotes boolean default true not null, + open_registration boolean default true not null, + enable_nsfw boolean default true not null, + community_creation_admin_only boolean default false not null, + require_email_verification boolean default false not null, + require_application boolean default true not null, + application_question text default 'to verify that you are human, please explain why you want to create an account on this site'::text, + private_instance boolean default false not null, + default_theme text default 'browser'::text not null, + default_post_listing_type text default 'Local'::text not null, + legal_information text, + hide_modlog_mod_names boolean default true not null, + application_email_admins boolean default false not null, + + -- Fields from lemmy.hjson + slur_filter_regex text, + actor_name_max_length int default 20 not null, + federation_enabled boolean default true not null, + federation_debug boolean default false not null, + federation_strict_allowlist boolean default true not null, + federation_http_fetch_retry_limit int default 25 not null, + federation_worker_count int default 64 not null, + captcha_enabled boolean default false not null, + captcha_difficulty varchar(255) default 'medium' not null, + + -- Time fields + published timestamp without time zone default now() not null, + updated timestamp without time zone +); + +-- local_site_rate_limit is its own table, so as to not go over 32 columns, and force diesel to use the 64-column-tables feature +create table local_site_rate_limit ( + id serial primary key, + local_site_id int references local_site on update cascade on delete cascade not null unique, + message int default 180 not null, + message_per_second int default 60 not null, + post int default 6 not null, + post_per_second int default 600 not null, + register int default 3 not null, + register_per_second int default 3600 not null, + image int default 6 not null, + image_per_second int default 3600 not null, + comment int default 6 not null, + comment_per_second int default 600 not null, + search int default 60 not null, + search_per_second int default 600 not null, + published timestamp without time zone default now() not null, + updated timestamp without time zone +); + +-- Insert the data into local_site +insert into local_site ( + site_id, + site_setup, + enable_downvotes, + open_registration, + enable_nsfw, + community_creation_admin_only, + require_email_verification, + require_application, + application_question, + private_instance, + default_theme, + default_post_listing_type, + legal_information, + hide_modlog_mod_names, + application_email_admins, + published, + updated +) +select + id, + true, -- Assume site if setup if there's already a site row + enable_downvotes, + open_registration, + enable_nsfw, + community_creation_admin_only, + require_email_verification, + require_application, + application_question, + private_instance, + default_theme, + default_post_listing_type, + legal_information, + hide_modlog_mod_names, + application_email_admins, + published, + updated +from site +order by id limit 1; + +-- Default here +insert into local_site_rate_limit ( + local_site_id +) +select id from local_site +order by id limit 1; + +-- Drop all those columns from site +alter table site + drop column enable_downvotes, + drop column open_registration, + drop column enable_nsfw, + drop column community_creation_admin_only, + drop column require_email_verification, + drop column require_application, + drop column application_question, + drop column private_instance, + drop column default_theme, + drop column default_post_listing_type, + drop column legal_information, + drop column hide_modlog_mod_names, + drop column application_email_admins; + diff --git a/src/code_migrations.rs b/src/code_migrations.rs index 347d50cb..159514a6 100644 --- a/src/code_migrations.rs +++ b/src/code_migrations.rs @@ -4,6 +4,7 @@ use diesel::{ sql_types::{Nullable, Text}, *, }; +use lemmy_api_common::lemmy_db_views::structs::SiteView; use lemmy_apub::{ generate_followers_url, generate_inbox_url, @@ -14,25 +15,29 @@ use lemmy_apub::{ }; use lemmy_db_schema::{ source::{ - comment::Comment, - community::{Community, CommunityForm}, - person::{Person, PersonForm}, - post::Post, - private_message::PrivateMessage, - site::{Site, SiteForm}, + comment::{Comment, CommentUpdateForm}, + community::{Community, CommunityUpdateForm}, + instance::Instance, + local_site::{LocalSite, LocalSiteInsertForm}, + local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitInsertForm}, + local_user::{LocalUser, LocalUserInsertForm}, + person::{Person, PersonInsertForm, PersonUpdateForm}, + post::{Post, PostUpdateForm}, + private_message::{PrivateMessage, PrivateMessageUpdateForm}, + site::{Site, SiteInsertForm, SiteUpdateForm}, }, traits::Crud, utils::naive_now, }; -use lemmy_utils::error::LemmyError; -use std::default::Default; +use lemmy_utils::{error::LemmyError, settings::structs::Settings}; use tracing::info; use url::Url; pub fn run_advanced_migrations( conn: &mut PgConnection, - protocol_and_hostname: &str, + settings: &Settings, ) -> Result<(), LemmyError> { + let protocol_and_hostname = &settings.get_protocol_and_hostname(); user_updates_2020_04_02(conn, protocol_and_hostname)?; community_updates_2020_04_02(conn, protocol_and_hostname)?; post_updates_2020_04_03(conn, protocol_and_hostname)?; @@ -42,6 +47,7 @@ pub fn run_advanced_migrations( apub_columns_2021_02_02(conn)?; instance_actor_2022_01_28(conn, protocol_and_hostname)?; regenerate_public_keys_2022_07_05(conn)?; + initialize_local_site_2022_10_10(conn, settings)?; Ok(()) } @@ -63,18 +69,16 @@ fn user_updates_2020_04_02( for cperson in &incorrect_persons { let keypair = generate_actor_keypair()?; - let form = PersonForm { - name: cperson.name.to_owned(), - actor_id: Some(generate_local_apub_endpoint( + let form = PersonUpdateForm::builder() + .actor_id(Some(generate_local_apub_endpoint( EndpointType::Person, &cperson.name, protocol_and_hostname, - )?), - private_key: Some(Some(keypair.private_key)), - public_key: Some(keypair.public_key), - last_refreshed_at: Some(naive_now()), - ..PersonForm::default() - }; + )?)) + .private_key(Some(Some(keypair.private_key))) + .public_key(Some(keypair.public_key)) + .last_refreshed_at(Some(naive_now())) + .build(); Person::update(conn, cperson.id, &form)?; } @@ -106,20 +110,12 @@ fn community_updates_2020_04_02( protocol_and_hostname, )?; - let form = CommunityForm { - name: ccommunity.name.to_owned(), - title: ccommunity.title.to_owned(), - description: Some(ccommunity.description.to_owned()), - hidden: Some(false), - actor_id: Some(community_actor_id.to_owned()), - local: Some(ccommunity.local), - private_key: Some(Some(keypair.private_key)), - public_key: Some(keypair.public_key), - last_refreshed_at: Some(naive_now()), - icon: Some(ccommunity.icon.to_owned()), - banner: Some(ccommunity.banner.to_owned()), - ..Default::default() - }; + let form = CommunityUpdateForm::builder() + .actor_id(Some(community_actor_id.to_owned())) + .private_key(Some(Some(keypair.private_key))) + .public_key(Some(keypair.public_key)) + .last_refreshed_at(Some(naive_now())) + .build(); Community::update(conn, ccommunity.id, &form)?; } @@ -149,7 +145,11 @@ fn post_updates_2020_04_03( &cpost.id.to_string(), protocol_and_hostname, )?; - Post::update_ap_id(conn, cpost.id, apub_id)?; + Post::update( + conn, + cpost.id, + &PostUpdateForm::builder().ap_id(Some(apub_id)).build(), + )?; } info!("{} post rows updated.", incorrect_posts.len()); @@ -177,7 +177,11 @@ fn comment_updates_2020_04_03( &ccomment.id.to_string(), protocol_and_hostname, )?; - Comment::update_ap_id(conn, ccomment.id, apub_id)?; + Comment::update( + conn, + ccomment.id, + &CommentUpdateForm::builder().ap_id(Some(apub_id)).build(), + )?; } info!("{} comment rows updated.", incorrect_comments.len()); @@ -205,7 +209,13 @@ fn private_message_updates_2020_05_05( &cpm.id.to_string(), protocol_and_hostname, )?; - PrivateMessage::update_ap_id(conn, cpm.id, apub_id)?; + PrivateMessage::update( + conn, + cpm.id, + &PrivateMessageUpdateForm::builder() + .ap_id(Some(apub_id)) + .build(), + )?; } info!("{} private message rows updated.", incorrect_pms.len()); @@ -295,22 +305,21 @@ fn instance_actor_2022_01_28( protocol_and_hostname: &str, ) -> Result<(), LemmyError> { info!("Running instance_actor_2021_09_29"); - if let Ok(site) = Site::read_local(conn) { + if let Ok(site_view) = SiteView::read_local(conn) { + let site = site_view.site; // if site already has public key, we dont need to do anything here if !site.public_key.is_empty() { return Ok(()); } let key_pair = generate_actor_keypair()?; let actor_id = Url::parse(protocol_and_hostname)?; - let site_form = SiteForm { - name: site.name, - actor_id: Some(actor_id.clone().into()), - last_refreshed_at: Some(naive_now()), - inbox_url: Some(generate_site_inbox_url(&actor_id.into())?), - private_key: Some(Some(key_pair.private_key)), - public_key: Some(key_pair.public_key), - ..Default::default() - }; + let site_form = SiteUpdateForm::builder() + .actor_id(Some(actor_id.clone().into())) + .last_refreshed_at(Some(naive_now())) + .inbox_url(Some(generate_site_inbox_url(&actor_id.into())?)) + .private_key(Some(Some(key_pair.private_key))) + .public_key(Some(key_pair.public_key)) + .build(); Site::update(conn, site.id, &site_form)?; } Ok(()) @@ -337,13 +346,10 @@ fn regenerate_public_keys_2022_07_05(conn: &mut PgConnection) -> Result<(), Lemm community_.name ); let key_pair = generate_actor_keypair()?; - let form = CommunityForm { - name: community_.name, - title: community_.title, - public_key: Some(key_pair.public_key), - private_key: Some(Some(key_pair.private_key)), - ..Default::default() - }; + let form = CommunityUpdateForm::builder() + .public_key(Some(key_pair.public_key)) + .private_key(Some(Some(key_pair.private_key))) + .build(); Community::update(conn, community_.id, &form)?; } } @@ -361,14 +367,111 @@ fn regenerate_public_keys_2022_07_05(conn: &mut PgConnection) -> Result<(), Lemm person_.name ); let key_pair = generate_actor_keypair()?; - let form = PersonForm { - name: person_.name, - public_key: Some(key_pair.public_key), - private_key: Some(Some(key_pair.private_key)), - ..Default::default() - }; + let form = PersonUpdateForm::builder() + .public_key(Some(key_pair.public_key)) + .private_key(Some(Some(key_pair.private_key))) + .build(); Person::update(conn, person_.id, &form)?; } } Ok(()) } + +/// This ensures that your local site is initialized and exists. +/// +/// If a site already exists, the DB migration should generate a local_site row. +/// This will only be run for brand new sites. +fn initialize_local_site_2022_10_10( + conn: &mut PgConnection, + settings: &Settings, +) -> Result<(), LemmyError> { + info!("Running initialize_local_site_2022_10_10"); + + // Check to see if local_site exists + if LocalSite::read(conn).is_ok() { + return Ok(()); + } + info!("No Local Site found, creating it."); + + let domain = settings + .get_hostname_without_port() + .expect("must have domain"); + + // Upsert this to the instance table + let instance = Instance::create(conn, &domain)?; + + if let Some(setup) = &settings.setup { + let person_keypair = generate_actor_keypair()?; + let person_actor_id = generate_local_apub_endpoint( + EndpointType::Person, + &setup.admin_username, + &settings.get_protocol_and_hostname(), + )?; + + // Register the user if there's a site setup + let person_form = PersonInsertForm::builder() + .name(setup.admin_username.to_owned()) + .admin(Some(true)) + .instance_id(instance.id) + .actor_id(Some(person_actor_id.clone())) + .private_key(Some(person_keypair.private_key)) + .public_key(person_keypair.public_key) + .inbox_url(Some(generate_inbox_url(&person_actor_id)?)) + .shared_inbox_url(Some(generate_shared_inbox_url(&person_actor_id)?)) + .build(); + let person_inserted = Person::create(conn, &person_form)?; + + let local_user_form = LocalUserInsertForm::builder() + .person_id(person_inserted.id) + .password_encrypted(setup.admin_password.to_owned()) + .email(setup.admin_email.to_owned()) + .build(); + LocalUser::create(conn, &local_user_form)?; + }; + + // Add an entry for the site table + let site_key_pair = generate_actor_keypair()?; + let site_actor_id = Url::parse(&settings.get_protocol_and_hostname())?; + + let site_form = SiteInsertForm::builder() + .name( + settings + .setup + .to_owned() + .map(|s| s.site_name) + .unwrap_or_else(|| "New Site".to_string()), + ) + .instance_id(instance.id) + .actor_id(Some(site_actor_id.clone().into())) + .last_refreshed_at(Some(naive_now())) + .inbox_url(Some(generate_site_inbox_url(&site_actor_id.into())?)) + .private_key(Some(site_key_pair.private_key)) + .public_key(Some(site_key_pair.public_key)) + .build(); + let site = Site::create(conn, &site_form)?; + + // Finally create the local_site row + let local_site_form = LocalSiteInsertForm::builder() + .site_id(site.id) + .site_setup(Some(settings.setup.is_some())) + .build(); + let local_site = LocalSite::create(conn, &local_site_form)?; + + // Create the rate limit table + let local_site_rate_limit_form = LocalSiteRateLimitInsertForm::builder() + // TODO these have to be set, because the database defaults are too low for the federation + // tests to pass, and there's no way to live update the rate limits without restarting the + // server. + // This can be removed once live rate limits are enabled. + .message(Some(999)) + .post(Some(999)) + .register(Some(999)) + .image(Some(999)) + .comment(Some(999)) + .search(Some(999)) + .local_site_id(local_site.id) + .build(); + LocalSiteRateLimit::create(conn, &local_site_rate_limit_form)?; + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 3dcc41e5..ffac400f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,3 @@ -#![recursion_limit = "512"] pub mod api_routes; pub mod code_migrations; pub mod root_span_builder; diff --git a/src/main.rs b/src/main.rs index 3c0bf3f0..9ef2f541 100644 --- a/src/main.rs +++ b/src/main.rs @@ -12,8 +12,13 @@ use diesel_migrations::EmbeddedMigrations; use doku::json::{AutoComments, Formatting}; use lemmy_api::match_websocket_operation; use lemmy_api_common::{ + lemmy_db_views::structs::SiteView, request::build_user_agent, - utils::{blocking, check_private_instance_and_federation_enabled}, + utils::{ + blocking, + check_private_instance_and_federation_enabled, + local_site_rate_limit_to_rate_limit_config, + }, }; use lemmy_api_crud::match_websocket_operation_crud; use lemmy_db_schema::{source::secret::Secret, utils::get_database_url_from_env}; @@ -77,12 +82,12 @@ async fn main() -> Result<(), LemmyError> { .unwrap_or_else(|_| panic!("Error connecting to {}", db_url)); // Run the migrations from code - let protocol_and_hostname = settings.get_protocol_and_hostname(); + let settings_cloned = settings.to_owned(); blocking(&pool, move |conn| { let _ = conn .run_pending_migrations(MIGRATIONS) .map_err(|_| LemmyError::from_message("Couldn't run migrations"))?; - run_advanced_migrations(conn, &protocol_and_hostname)?; + run_advanced_migrations(conn, &settings_cloned)?; Ok(()) as Result<(), LemmyError> }) .await??; @@ -93,16 +98,32 @@ async fn main() -> Result<(), LemmyError> { scheduled_tasks::setup(pool2).expect("Couldn't set up scheduled_tasks"); }); + // Initialize the secrets + let conn = &mut pool.get()?; + let secret = Secret::init(conn).expect("Couldn't initialize secrets."); + + // Make sure the local site is set up. + let site_view = SiteView::read_local(conn).expect("local site not set up"); + let local_site = site_view.local_site; + let federation_enabled = local_site.federation_enabled; + + if federation_enabled { + println!("federation enabled, host is {}", &settings.hostname); + } + + check_private_instance_and_federation_enabled(&local_site)?; + // Set up the rate limiter + let rate_limit_config = + local_site_rate_limit_to_rate_limit_config(&site_view.local_site_rate_limit); + + // TODO this isn't live-updating + // https://github.com/LemmyNet/lemmy/issues/2508 let rate_limiter = RateLimit { rate_limiter: Arc::new(Mutex::new(RateLimiter::default())), - rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(), + rate_limit_config, }; - // Initialize the secrets - let conn = &mut pool.get()?; - let secret = Secret::init(conn).expect("Couldn't initialize secrets."); - println!( "Starting http server at {}:{}", settings.bind, settings.port @@ -130,8 +151,6 @@ async fn main() -> Result<(), LemmyError> { .with(TracingMiddleware::default()) .build(); - check_private_instance_and_federation_enabled(&pool, &settings).await?; - let chat_server = ChatServer::startup( pool.clone(), rate_limiter.clone(), @@ -161,11 +180,15 @@ async fn main() -> Result<(), LemmyError> { .app_data(Data::new(rate_limiter.clone())) // The routes .configure(|cfg| api_routes::config(cfg, &rate_limiter)) - .configure(|cfg| lemmy_apub::http::routes::config(cfg, &settings)) + .configure(|cfg| { + if federation_enabled { + lemmy_apub::http::routes::config(cfg); + webfinger::config(cfg); + } + }) .configure(feeds::config) .configure(|cfg| images::config(cfg, pictrs_client.clone(), &rate_limiter)) .configure(nodeinfo::config) - .configure(|cfg| webfinger::config(cfg, &settings)) }) .bind((settings_bind.bind, settings_bind.port))? .run()