]> Untitled Git - lemmy.git/commitdiff
Moving settings to Database. (#2492)
authorDessalines <dessalines@users.noreply.github.com>
Thu, 27 Oct 2022 09:24:07 +0000 (05:24 -0400)
committerGitHub <noreply@github.com>
Thu, 27 Oct 2022 09:24:07 +0000 (09:24 +0000)
* 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.

179 files changed:
Cargo.lock
api_tests/.eslintrc.json
api_tests/.prettierrc.js
api_tests/package.json
api_tests/src/comment.spec.ts
api_tests/src/community.spec.ts
api_tests/src/follow.spec.ts
api_tests/src/post.spec.ts
api_tests/src/private_message.spec.ts
api_tests/src/shared.ts
api_tests/src/user.spec.ts
api_tests/yarn.lock
config/config.hjson
config/defaults.hjson
crates/api/src/comment/like.rs
crates/api/src/comment_report/create.rs
crates/api/src/community/hide.rs
crates/api/src/lib.rs
crates/api/src/local_user/add_admin.rs
crates/api/src/local_user/ban_person.rs
crates/api/src/local_user/get_captcha.rs
crates/api/src/local_user/login.rs
crates/api/src/local_user/notifications/mark_mention_read.rs
crates/api/src/local_user/notifications/mark_reply_read.rs
crates/api/src/local_user/save_settings.rs
crates/api/src/local_user/verify_email.rs
crates/api/src/post/like.rs
crates/api/src/post/lock.rs
crates/api/src/post/sticky.rs
crates/api/src/post_report/create.rs
crates/api/src/private_message/mark_read.rs
crates/api/src/private_message_report/create.rs
crates/api/src/site/leave_admin.rs
crates/api/src/site/mod_log.rs
crates/api/src/site/registration_applications/approve.rs
crates/api/src/site/registration_applications/list.rs
crates/api/src/site/registration_applications/unread_count.rs
crates/api/src/site/resolve_object.rs
crates/api/src/site/search.rs
crates/api_common/Cargo.toml
crates/api_common/src/site.rs
crates/api_common/src/utils.rs
crates/api_crud/src/comment/create.rs
crates/api_crud/src/comment/delete.rs
crates/api_crud/src/comment/list.rs
crates/api_crud/src/comment/read.rs
crates/api_crud/src/comment/remove.rs
crates/api_crud/src/comment/update.rs
crates/api_crud/src/community/create.rs
crates/api_crud/src/community/delete.rs
crates/api_crud/src/community/list.rs
crates/api_crud/src/community/read.rs
crates/api_crud/src/community/remove.rs
crates/api_crud/src/community/update.rs
crates/api_crud/src/post/create.rs
crates/api_crud/src/post/delete.rs
crates/api_crud/src/post/list.rs
crates/api_crud/src/post/read.rs
crates/api_crud/src/post/remove.rs
crates/api_crud/src/post/update.rs
crates/api_crud/src/private_message/create.rs
crates/api_crud/src/private_message/delete.rs
crates/api_crud/src/private_message/update.rs
crates/api_crud/src/site/create.rs
crates/api_crud/src/site/read.rs
crates/api_crud/src/site/update.rs
crates/api_crud/src/user/create.rs
crates/api_crud/src/user/read.rs
crates/apub/src/activities/block/block_user.rs
crates/apub/src/activities/block/undo_block_user.rs
crates/apub/src/activities/community/add_mod.rs
crates/apub/src/activities/community/announce.rs
crates/apub/src/activities/community/remove_mod.rs
crates/apub/src/activities/community/report.rs
crates/apub/src/activities/community/update.rs
crates/apub/src/activities/create_or_update/comment.rs
crates/apub/src/activities/create_or_update/post.rs
crates/apub/src/activities/create_or_update/private_message.rs
crates/apub/src/activities/deletion/delete.rs
crates/apub/src/activities/deletion/delete_user.rs
crates/apub/src/activities/deletion/mod.rs
crates/apub/src/activities/deletion/undo_delete.rs
crates/apub/src/activities/following/accept.rs
crates/apub/src/activities/following/follow.rs
crates/apub/src/activities/following/undo_follow.rs
crates/apub/src/activities/mod.rs
crates/apub/src/activities/voting/undo_vote.rs
crates/apub/src/activities/voting/vote.rs
crates/apub/src/collections/community_moderators.rs
crates/apub/src/fetcher/webfinger.rs
crates/apub/src/http/routes.rs
crates/apub/src/http/site.rs
crates/apub/src/lib.rs
crates/apub/src/objects/comment.rs
crates/apub/src/objects/community.rs
crates/apub/src/objects/instance.rs
crates/apub/src/objects/mod.rs
crates/apub/src/objects/person.rs
crates/apub/src/objects/post.rs
crates/apub/src/objects/private_message.rs
crates/apub/src/protocol/objects/group.rs
crates/db_schema/Cargo.toml
crates/db_schema/src/aggregates/comment_aggregates.rs
crates/db_schema/src/aggregates/community_aggregates.rs
crates/db_schema/src/aggregates/person_aggregates.rs
crates/db_schema/src/aggregates/post_aggregates.rs
crates/db_schema/src/aggregates/site_aggregates.rs
crates/db_schema/src/impls/activity.rs
crates/db_schema/src/impls/actor_language.rs
crates/db_schema/src/impls/comment.rs
crates/db_schema/src/impls/comment_reply.rs
crates/db_schema/src/impls/community.rs
crates/db_schema/src/impls/email_verification.rs
crates/db_schema/src/impls/federation_allowlist.rs [new file with mode: 0644]
crates/db_schema/src/impls/federation_blocklist.rs [new file with mode: 0644]
crates/db_schema/src/impls/instance.rs [new file with mode: 0644]
crates/db_schema/src/impls/local_site.rs [new file with mode: 0644]
crates/db_schema/src/impls/local_site_rate_limit.rs [new file with mode: 0644]
crates/db_schema/src/impls/local_user.rs
crates/db_schema/src/impls/mod.rs
crates/db_schema/src/impls/moderator.rs
crates/db_schema/src/impls/password_reset_request.rs
crates/db_schema/src/impls/person.rs
crates/db_schema/src/impls/person_mention.rs
crates/db_schema/src/impls/post.rs
crates/db_schema/src/impls/private_message.rs
crates/db_schema/src/impls/registration_application.rs
crates/db_schema/src/impls/site.rs
crates/db_schema/src/lib.rs
crates/db_schema/src/newtypes.rs
crates/db_schema/src/schema.rs
crates/db_schema/src/source/activity.rs
crates/db_schema/src/source/comment.rs
crates/db_schema/src/source/comment_reply.rs
crates/db_schema/src/source/community.rs
crates/db_schema/src/source/federation_allowlist.rs [new file with mode: 0644]
crates/db_schema/src/source/federation_blocklist.rs [new file with mode: 0644]
crates/db_schema/src/source/instance.rs [new file with mode: 0644]
crates/db_schema/src/source/local_site.rs [new file with mode: 0644]
crates/db_schema/src/source/local_site_rate_limit.rs [new file with mode: 0644]
crates/db_schema/src/source/local_user.rs
crates/db_schema/src/source/mod.rs
crates/db_schema/src/source/person.rs
crates/db_schema/src/source/person_mention.rs
crates/db_schema/src/source/post.rs
crates/db_schema/src/source/private_message.rs
crates/db_schema/src/source/registration_application.rs
crates/db_schema/src/source/site.rs
crates/db_schema/src/traits.rs
crates/db_schema/src/utils.rs
crates/db_views/Cargo.toml
crates/db_views/src/comment_report_view.rs
crates/db_views/src/comment_view.rs
crates/db_views/src/post_report_view.rs
crates/db_views/src/post_view.rs
crates/db_views/src/private_message_report_view.rs
crates/db_views/src/registration_application_view.rs
crates/db_views/src/site_view.rs
crates/db_views/src/structs.rs
crates/routes/src/nodeinfo.rs
crates/routes/src/webfinger.rs
crates/utils/Cargo.toml
crates/utils/src/rate_limit/mod.rs
crates/utils/src/settings/mod.rs
crates/utils/src/settings/structs.rs
crates/utils/src/test.rs
crates/utils/src/utils.rs
crates/utils/translations
crates/websocket/src/send.rs
docker/federation/lemmy_alpha.hjson
docker/federation/lemmy_beta.hjson
docker/federation/lemmy_delta.hjson
docker/federation/lemmy_epsilon.hjson
docker/federation/lemmy_gamma.hjson
migrations/2022-10-06-183632_move_blocklist_to_db/down.sql [new file with mode: 0644]
migrations/2022-10-06-183632_move_blocklist_to_db/up.sql [new file with mode: 0644]
src/code_migrations.rs
src/lib.rs
src/main.rs

index c3743cac1bc4307cad16c794ba4d411938a76f21..e726ae25e2a0651a6f8415d899267a38785d3337 100644 (file)
@@ -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",
 ]
index aec9f66ed28de5a29e561801125b4d88947bb6f4..6759f712f8d56b4600f744df4053c337bc40e3bb 100644 (file)
@@ -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": {
index 5983e1a19b8ba1de655dce69df12670a2c180be3..8d36af38c1c49fcab7f9dff4a8878911f9e32a98 100644 (file)
@@ -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,
 });
index 718f454cb57428265b48c1cf901732c6f6675e48..48d873990b1671a6a8bda36151f776fecb96a1ea 100644 (file)
   "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"
   }
 }
index ca9cc3da5c5af9ee5e6a32ba5290008183137de5..5fb6bc80e831657b067c66dcd2014005424f8600 100644 (file)
@@ -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.');
-}
-
index 381b546b306499dcf549ccb642b2ffec4c22d673..0ed50ec484672becec82ff5257c0ac288eaf4965 100644 (file)
@@ -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);
 });
index 54fc16669d2e66572db31de619220ecde02422bb..f80b40de858d5e7b7427d68b48984aa3b4f3f648 100644 (file)
@@ -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);
 
index edf85ebd2537ac6794edd44b7d616388b86f6f48..d0e42c726ba52f3d26ac024e1c07bbba8d13c040 100644 (file)
@@ -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);
 });
index 0051f2c772c31941fade73b7f5fedcda0eb49814..751f722303445c4fbd62235f7619b5e614a1b75e 100644 (file)
@@ -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(
index 7286ea5af2f70658aa745f06f7f7368eac2f12a6..9bf232065e0a71988e71d08d0d9c9a6505b24235 100644 (file)
@@ -1,4 +1,4 @@
-import {None, Some, Option} from '@sniptt/monads';
+import { None, Some, Option } from "@sniptt/monads";
 import {
   Login,
   LoginResponse,
@@ -63,8 +63,8 @@ import {
   EditSite,
   CommentSortType,
   GetComments,
-  GetCommentsResponse
-} from 'lemmy-js-client';
+  GetCommentsResponse,
+} from "lemmy-js-client";
 
 export interface API {
   client: LemmyHttp;
@@ -72,59 +72,59 @@ export interface API {
 }
 
 export let alpha: API = {
-  client: new LemmyHttp('http://127.0.0.1:8541'),
+  client: new LemmyHttp("http://127.0.0.1:8541"),
   auth: None,
 };
 
 export let beta: API = {
-  client: new LemmyHttp('http://127.0.0.1:8551'),
+  client: new LemmyHttp("http://127.0.0.1:8551"),
   auth: None,
 };
 
 export let gamma: API = {
-  client: new LemmyHttp('http://127.0.0.1:8561'),
+  client: new LemmyHttp("http://127.0.0.1:8561"),
   auth: None,
 };
 
 export let delta: API = {
-  client: new LemmyHttp('http://127.0.0.1:8571'),
+  client: new LemmyHttp("http://127.0.0.1:8571"),
   auth: None,
 };
 
 export let epsilon: API = {
-  client: new LemmyHttp('http://127.0.0.1:8581'),
+  client: new LemmyHttp("http://127.0.0.1:8581"),
   auth: None,
 };
 
-const password = 'lemmylemmy'
+const password = "lemmylemmy";
 
 export async function setupLogins() {
   let formAlpha = new Login({
-    username_or_email: 'lemmy_alpha',
+    username_or_email: "lemmy_alpha",
     password,
   });
   let resAlpha = alpha.client.login(formAlpha);
 
   let formBeta = new Login({
-    username_or_email: 'lemmy_beta',
+    username_or_email: "lemmy_beta",
     password,
   });
   let resBeta = beta.client.login(formBeta);
 
   let formGamma = new Login({
-    username_or_email: 'lemmy_gamma',
+    username_or_email: "lemmy_gamma",
     password,
   });
   let resGamma = gamma.client.login(formGamma);
 
   let formDelta = new Login({
-    username_or_email: 'lemmy_delta',
+    username_or_email: "lemmy_delta",
     password,
   });
   let resDelta = delta.client.login(formDelta);
 
   let formEpsilon = new Login({
-    username_or_email: 'lemmy_epsilon',
+    username_or_email: "lemmy_epsilon",
     password,
   });
   let resEpsilon = epsilon.client.login(formEpsilon);
@@ -145,6 +145,8 @@ export async function setupLogins() {
 
   // Registration applications are now enabled by default, need to disable them
   let editSiteForm = new EditSite({
+    require_application: Some(false),
+    federation_debug: Some(true),
     name: None,
     sidebar: None,
     description: None,
@@ -155,23 +157,74 @@ export async function setupLogins() {
     enable_nsfw: None,
     community_creation_admin_only: None,
     require_email_verification: None,
-    require_application: Some(false),
     application_question: None,
     private_instance: None,
     default_theme: None,
-    legal_information: None,
     default_post_listing_type: None,
+    legal_information: None,
+    application_email_admins: None,
+    hide_modlog_mod_names: None,
+    discussion_languages: None,
+    slur_filter_regex: None,
+    actor_name_max_length: None,
+    rate_limit_message: Some(999),
+    rate_limit_message_per_second: None,
+    rate_limit_post: Some(999),
+    rate_limit_post_per_second: None,
+    rate_limit_register: Some(999),
+    rate_limit_register_per_second: None,
+    rate_limit_image: Some(999),
+    rate_limit_image_per_second: None,
+    rate_limit_comment: Some(999),
+    rate_limit_comment_per_second: None,
+    rate_limit_search: Some(999),
+    rate_limit_search_per_second: None,
+    federation_enabled: None,
+    federation_strict_allowlist: None,
+    federation_http_fetch_retry_limit: None,
+    federation_worker_count: None,
+    captcha_enabled: None,
+    captcha_difficulty: None,
+    allowed_instances: None,
+    blocked_instances: None,
     auth: "",
   });
+
+  // Set the blocks and auths for each
   editSiteForm.auth = alpha.auth.unwrap();
+  editSiteForm.allowed_instances = Some([
+    "lemmy-beta",
+    "lemmy-gamma",
+    "lemmy-delta",
+    "lemmy-epsilon",
+  ]);
   await alpha.client.editSite(editSiteForm);
+
   editSiteForm.auth = beta.auth.unwrap();
+  editSiteForm.allowed_instances = Some([
+    "lemmy-alpha",
+    "lemmy-gamma",
+    "lemmy-delta",
+    "lemmy-epsilon",
+  ]);
   await beta.client.editSite(editSiteForm);
+
   editSiteForm.auth = gamma.auth.unwrap();
+  editSiteForm.allowed_instances = Some([
+    "lemmy-alpha",
+    "lemmy-beta",
+    "lemmy-delta",
+    "lemmy-epsilon",
+  ]);
   await gamma.client.editSite(editSiteForm);
+
+  editSiteForm.allowed_instances = Some(["lemmy-beta"]);
   editSiteForm.auth = delta.auth.unwrap();
   await delta.client.editSite(editSiteForm);
+
   editSiteForm.auth = epsilon.auth.unwrap();
+  editSiteForm.allowed_instances = Some([]);
+  editSiteForm.blocked_instances = Some(["lemmy-alpha"]);
   await epsilon.client.editSite(editSiteForm);
 
   // Create the main alpha/beta communities
@@ -185,7 +238,7 @@ export async function createPost(
 ): Promise<PostResponse> {
   let name = randomString(5);
   let body = Some(randomString(10));
-  let url = Some('https://google.com/');
+  let url = Some("https://google.com/");
   let form = new CreatePost({
     name,
     url,
@@ -194,12 +247,13 @@ export async function createPost(
     community_id,
     nsfw: None,
     honeypot: None,
+    language_id: None,
   });
   return api.client.createPost(form);
 }
 
 export async function editPost(api: API, post: Post): Promise<PostResponse> {
-  let name = Some('A jest test federated post, updated');
+  let name = Some("A jest test federated post, updated");
   let form = new EditPost({
     name,
     post_id: post.id,
@@ -207,6 +261,7 @@ export async function editPost(api: API, post: Post): Promise<PostResponse> {
     nsfw: None,
     url: None,
     body: None,
+    language_id: None,
   });
   return api.client.editPost(form);
 }
@@ -342,7 +397,7 @@ export async function resolveBetaCommunity(
 ): Promise<ResolveObjectResponse> {
   // Use short-hand search url
   let form = new ResolveObject({
-    q: '!main@lemmy-beta:8551',
+    q: "!main@lemmy-beta:8551",
     auth: api.auth,
   });
   return api.client.resolveObject(form);
@@ -415,7 +470,7 @@ export async function followCommunity(
   let form = new FollowCommunity({
     community_id,
     follow,
-    auth: api.auth.unwrap()
+    auth: api.auth.unwrap(),
   });
   return api.client.followCommunity(form);
 }
@@ -428,7 +483,7 @@ export async function likePost(
   let form = new CreatePostLike({
     post_id: post.id,
     score: score,
-    auth: api.auth.unwrap()
+    auth: api.auth.unwrap(),
   });
 
   return api.client.likePost(form);
@@ -438,13 +493,14 @@ export async function createComment(
   api: API,
   post_id: number,
   parent_id: Option<number>,
-  content = 'a jest test comment'
+  content = "a jest test comment"
 ): Promise<CommentResponse> {
   let form = new CreateComment({
     content,
     post_id,
     parent_id,
     form_id: None,
+    language_id: None,
     auth: api.auth.unwrap(),
   });
   return api.client.createComment(form);
@@ -453,13 +509,15 @@ export async function createComment(
 export async function editComment(
   api: API,
   comment_id: number,
-  content = 'A jest test federated comment update'
+  content = Some("A jest test federated comment update")
 ): Promise<CommentResponse> {
   let form = new EditComment({
     content,
     comment_id,
     form_id: None,
-    auth: api.auth.unwrap()
+    language_id: None,
+    distinguished: None,
+    auth: api.auth.unwrap(),
   });
   return api.client.editComment(form);
 }
@@ -491,7 +549,9 @@ export async function removeComment(
   return api.client.removeComment(form);
 }
 
-export async function getMentions(api: API): Promise<GetPersonMentionsResponse> {
+export async function getMentions(
+  api: API
+): Promise<GetPersonMentionsResponse> {
   let form = new GetPersonMentions({
     sort: Some(CommentSortType.New),
     unread_only: Some(false),
@@ -519,7 +579,7 @@ export async function createCommunity(
   api: API,
   name_: string = randomString(5)
 ): Promise<CommunityResponse> {
-  let description = Some('a sample description');
+  let description = Some("a sample description");
   let form = new CreateCommunity({
     name: name_,
     title: name_,
@@ -577,7 +637,7 @@ export async function createPrivateMessage(
   api: API,
   recipient_id: number
 ): Promise<PrivateMessageResponse> {
-  let content = 'A jest test federated private message';
+  let content = "A jest test federated private message";
   let form = new CreatePrivateMessage({
     content,
     recipient_id,
@@ -590,7 +650,7 @@ export async function editPrivateMessage(
   api: API,
   private_message_id: number
 ): Promise<PrivateMessageResponse> {
-  let updatedContent = 'A jest test federated private message edited';
+  let updatedContent = "A jest test federated private message edited";
   let form = new EditPrivateMessage({
     content: updatedContent,
     private_message_id,
@@ -630,18 +690,18 @@ export async function registerUser(
   return api.client.register(form);
 }
 
-export async function saveUserSettingsBio(
-  api: API
-): Promise<LoginResponse> {
+export async function saveUserSettingsBio(api: API): Promise<LoginResponse> {
   let form = new SaveUserSettings({
     show_nsfw: Some(true),
-    theme: Some('darkly'),
+    theme: Some("darkly"),
     default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Active)),
-    default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)),
-    lang: Some('en'),
+    default_listing_type: Some(
+      Object.keys(ListingType).indexOf(ListingType.All)
+    ),
+    interface_language: Some("en"),
     show_avatars: Some(true),
     send_notifications_to_email: Some(false),
-    bio: Some('a changed bio'),
+    bio: Some("a changed bio"),
     avatar: None,
     banner: None,
     display_name: None,
@@ -652,6 +712,7 @@ export async function saveUserSettingsBio(
     show_bot_accounts: None,
     show_new_post_notifs: None,
     bot_account: None,
+    discussion_languages: None,
     auth: api.auth.unwrap(),
   });
   return saveUserSettings(api, form);
@@ -660,18 +721,20 @@ export async function saveUserSettingsBio(
 export async function saveUserSettingsFederated(
   api: API
 ): Promise<LoginResponse> {
-  let avatar = Some('https://image.flaticon.com/icons/png/512/35/35896.png');
-  let banner = Some('https://image.flaticon.com/icons/png/512/36/35896.png');
-  let bio = Some('a changed bio');
+  let avatar = Some("https://image.flaticon.com/icons/png/512/35/35896.png");
+  let banner = Some("https://image.flaticon.com/icons/png/512/36/35896.png");
+  let bio = Some("a changed bio");
   let form = new SaveUserSettings({
     show_nsfw: Some(false),
-    theme: Some(''),
+    theme: Some(""),
     default_sort_type: Some(Object.keys(SortType).indexOf(SortType.Hot)),
-    default_listing_type: Some(Object.keys(ListingType).indexOf(ListingType.All)),
-    lang: Some(''),
+    default_listing_type: Some(
+      Object.keys(ListingType).indexOf(ListingType.All)
+    ),
+    interface_language: Some(""),
     avatar,
     banner,
-    display_name: Some('user321'),
+    display_name: Some("user321"),
     show_avatars: Some(false),
     send_notifications_to_email: Some(false),
     bio,
@@ -682,6 +745,7 @@ export async function saveUserSettingsFederated(
     bot_account: None,
     show_bot_accounts: None,
     show_new_post_notifs: None,
+    discussion_languages: None,
     auth: api.auth.unwrap(),
   });
   return await saveUserSettings(alpha, form);
@@ -694,19 +758,15 @@ export async function saveUserSettings(
   return api.client.saveUserSettings(form);
 }
 
-export async function deleteUser(
-  api: API
-): Promise<DeleteAccountResponse> {
+export async function deleteUser(api: API): Promise<DeleteAccountResponse> {
   let form = new DeleteAccount({
     auth: api.auth.unwrap(),
-    password
+    password,
   });
   return api.client.deleteAccount(form);
 }
 
-export async function getSite(
-  api: API
-): Promise<GetSiteResponse> {
+export async function getSite(api: API): Promise<GetSiteResponse> {
   let form = new GetSite({
     auth: api.auth,
   });
@@ -725,14 +785,12 @@ export async function listPrivateMessages(
   return api.client.getPrivateMessages(form);
 }
 
-export async function unfollowRemotes(
-  api: API
-): Promise<GetSiteResponse> {
+export async function unfollowRemotes(api: API): Promise<GetSiteResponse> {
   // Unfollow all remote communities
   let site = await getSite(api);
-  let remoteFollowed = site.my_user.unwrap().follows.filter(
-    c => c.community.local == false
-  );
+  let remoteFollowed = site.my_user
+    .unwrap()
+    .follows.filter(c => c.community.local == false);
   for (let cu of remoteFollowed) {
     await followCommunity(api, false, cu.community.id);
   }
@@ -743,7 +801,11 @@ export async function unfollowRemotes(
 export async function followBeta(api: API): Promise<CommunityResponse> {
   let betaCommunity = (await resolveBetaCommunity(api)).community;
   if (betaCommunity.isSome()) {
-    let follow = await followCommunity(api, true, betaCommunity.unwrap().community.id);
+    let follow = await followCommunity(
+      api,
+      true,
+      betaCommunity.unwrap().community.id
+    );
     return follow;
   } else {
     return Promise.reject("no community worked");
@@ -763,7 +825,9 @@ export async function reportPost(
   return api.client.createPostReport(form);
 }
 
-export async function listPostReports(api: API): Promise<ListPostReportsResponse> {
+export async function listPostReports(
+  api: API
+): Promise<ListPostReportsResponse> {
   let form = new ListPostReports({
     auth: api.auth.unwrap(),
     page: None,
@@ -787,7 +851,9 @@ export async function reportComment(
   return api.client.createCommentReport(form);
 }
 
-export async function listCommentReports(api: API): Promise<ListCommentReportsResponse> {
+export async function listCommentReports(
+  api: API
+): Promise<ListCommentReportsResponse> {
   let form = new ListCommentReports({
     page: None,
     limit: None,
@@ -798,7 +864,7 @@ export async function listCommentReports(api: API): Promise<ListCommentReportsRe
   return api.client.listCommentReports(form);
 }
 
-export function delay(millis: number = 500) {
+export function delay(millis = 500) {
   return new Promise(resolve => setTimeout(resolve, millis));
 }
 
@@ -811,8 +877,9 @@ export function wrapper(form: any): string {
 }
 
 export function randomString(length: number): string {
-  var result = '';
-  var characters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_';
+  var result = "";
+  var characters =
+    "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
   var charactersLength = characters.length;
   for (var i = 0; i < length; i++) {
     result += characters.charAt(Math.floor(Math.random() * charactersLength));
index 3dd66e581d30c59d25c4431bce65f1cf305ae60a..cd1325181b478f5f51e0405ba528e2d69fe5563e 100644 (file)
@@ -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
+  );
 });
index 97ec5c37ab34fa8edc0bae156cf25fd62b422d6f..9797ac697e1d4b0059fc5acbb0497c5e3f6f5898 100644 (file)
@@ -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==
   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==
     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"
   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"
     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==
     "@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"
     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"
   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"
   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==
index 8d2ae84c73f6cb27b1f55ae3e1a9e94295885726..252fca25044f07c0075e52174f23c88f3d37d2ce 100644 (file)
@@ -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?)
-    '''
 }
index b4e1e3dfccb8e1f08e266331bd1c387519a9727e..e92117787e674a0d51e541a51d594726e7487a70 100644 (file)
     # 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)
     # 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
   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
 }
index 4e527dd0acecc52a49eead11caa0623a40c0ea36..754072ee204daab38f3dc1ca6beb5bdb808d7188 100644 (file)
@@ -16,6 +16,7 @@ use lemmy_db_schema::{
   source::{
     comment::{CommentLike, CommentLikeForm},
     comment_reply::CommentReply,
+    local_site::LocalSite,
   },
   traits::Likeable,
 };
@@ -35,13 +36,14 @@ impl Perform for CreateCommentLike {
     websocket_id: Option<ConnectionId>,
   ) -> Result<CommentResponse, LemmyError> {
     let data: &CreateCommentLike = self;
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
     let local_user_view =
       get_local_user_view_from_jwt(&data.auth, context.pool(), context.secret()).await?;
 
     let mut recipient_ids = Vec::<LocalUserId>::new();
 
     // Don't do a downvote if site has downvotes disabled
-    check_downvotes_enabled(data.score, context.pool()).await?;
+    check_downvotes_enabled(data.score, &local_site)?;
 
     let comment_id = data.comment_id;
     let orig_comment = blocking(context.pool(), move |conn| {
index 6d21f066b4e29012cfe93659f8d8e828ea00c727..5a93fa02e6943c81b2e7f7fb80ccdfe108995789 100644 (file)
@@ -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;
index a038e63106d2d3de0da0fde21a4dbaa6114d90b4..278d492d73a3817b7d8eeac45763f96239f1d6c2 100644 (file)
@@ -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,
index e3419f3843daf03a898120dec95edd036cf22cc3..595ab10f8059d3ac19781d8b6332e99b54d4182c 100644 (file)
@@ -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();
 
index b44b210b7bb18fa9287ab81392235ac6ab81ca5b..2561906e5b395fd81b4648181e735c83cdb3f615 100644 (file)
@@ -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"))?;
index fce9b076d8193e6b3f6e31d91716338b0c7a3faa..f9dc83e0dd5265280fe50c2eda6cc7dbac306d0f 100644 (file)
@@ -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 {
index b63550362a6b58461f18d5adbfbcf9a24a279dab..1d2511f56f9b79a55648e4b88f00438ee8a11d09 100644 (file)
@@ -2,8 +2,11 @@ use crate::{captcha_as_wav_base64, Perform};
 use actix_web::web::Data;
 use captcha::{gen, Difficulty};
 use chrono::Duration;
-use lemmy_api_common::person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse};
-use lemmy_db_schema::utils::naive_now;
+use lemmy_api_common::{
+  person::{CaptchaResponse, GetCaptcha, GetCaptchaResponse},
+  utils::blocking,
+};
+use lemmy_db_schema::{source::local_site::LocalSite, utils::naive_now};
 use lemmy_utils::{error::LemmyError, ConnectionId};
 use lemmy_websocket::{messages::CaptchaItem, LemmyContext};
 
@@ -17,13 +20,13 @@ impl Perform for GetCaptcha {
     context: &Data<LemmyContext>,
     _websocket_id: Option<ConnectionId>,
   ) -> Result<Self::Response, LemmyError> {
-    let captcha_settings = &context.settings().captcha;
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
 
-    if !captcha_settings.enabled {
+    if !local_site.captcha_enabled {
       return Ok(GetCaptchaResponse { ok: None });
     }
 
-    let captcha = gen(match captcha_settings.difficulty.as_str() {
+    let captcha = gen(match local_site.captcha_difficulty.as_str() {
       "easy" => Difficulty::Easy,
       "hard" => Difficulty::Hard,
       _ => Difficulty::Medium,
index 637f0d88dc9915f3e8b81429dcfd52e272f029e1..990424c0a871495f964d6b372f4b148ff905e8ad 100644 (file)
@@ -5,7 +5,7 @@ use lemmy_api_common::{
   person::{Login, LoginResponse},
   utils::{blocking, check_registration_application, check_user_valid},
 };
-use lemmy_db_schema::source::site::Site;
+use lemmy_db_schema::source::local_site::LocalSite;
 use lemmy_db_views::structs::LocalUserView;
 use lemmy_utils::{claims::Claims, error::LemmyError, ConnectionId};
 use lemmy_websocket::LemmyContext;
@@ -22,6 +22,8 @@ impl Perform for Login {
   ) -> Result<LoginResponse, LemmyError> {
     let data: &Login = self;
 
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
+
     // Fetch that username / email
     let username_or_email = data.username_or_email.clone();
     let local_user_view = blocking(context.pool(), move |conn| {
@@ -45,12 +47,11 @@ impl Perform for Login {
       local_user_view.person.deleted,
     )?;
 
-    let site = blocking(context.pool(), Site::read_local).await??;
-    if site.require_email_verification && !local_user_view.local_user.email_verified {
+    if local_site.require_email_verification && !local_user_view.local_user.email_verified {
       return Err(LemmyError::from_message("email_not_verified"));
     }
 
-    check_registration_application(&site, &local_user_view, context.pool()).await?;
+    check_registration_application(&local_user_view, &local_site, context.pool()).await?;
 
     // Return the jwt
     Ok(LoginResponse {
index 2179ada05db9714a12e86c39553de494379a3cb8..134b4311e6d0f2ea9969dc3e1149532a6fa00a6f 100644 (file)
@@ -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;
index 3e502f39c5d0e6b1122612caf42c7528ab57b413..e270d5470f0754ce49cbce2e991141cdf6d63b5d 100644 (file)
@@ -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;
index ccb1a340dc73448b1376ee6a31ea2408338afaa3..eac1eef0a20f1d4ce2fef8b79ca21b3412a69cc2 100644 (file)
@@ -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)
index 63a998915555be0b713b9f197da74afbf107444d..e4f43af1544429014fb5d22a0dfa94aaaafc9c20 100644 (file)
@@ -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)
index ef9546753c42eaf6cf6b1e8a73eaa81b5f3708d4..ee29a021f8ea71c56c7770d8c92f7584fd8e5f08 100644 (file)
@@ -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;
index 1e11117f2f2703619db49acce7954fbf22230480..5ce26c5e46cb38ce7e77c337b1a3260aac317a37 100644 (file)
@@ -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();
index 952eed6f711bae3318cebbde500ac39d1e13ca70..629de9d664b3c3ffdd724d1cf91489ec7bfd4c33 100644 (file)
@@ -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();
index 6843bcd3b0c053ce1a9cd2dc6cca9c8e79d5e547..24379f46f379e67c5bad7241af09c2b69af76315 100644 (file)
@@ -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;
index cdf38b80ea314a9e0eefab8e587824fd5998095e..6a5737ceadfe57e0c8b0310a69f4f138327a00ab 100644 (file)
@@ -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"))?;
index ee78af8046fceb4c917f6a53d3f8672d1c2fb417..8e661b79a114b186a95d4913ecaccf89018c6476 100644 (file)
@@ -7,6 +7,7 @@ use lemmy_api_common::{
 use lemmy_db_schema::{
   newtypes::CommunityId,
   source::{
+    local_site::LocalSite,
     private_message::PrivateMessage,
     private_message_report::{PrivateMessageReport, PrivateMessageReportForm},
   },
@@ -28,9 +29,10 @@ impl Perform for CreatePrivateMessageReport {
   ) -> Result<Self::Response, LemmyError> {
     let local_user_view =
       get_local_user_view_from_jwt(&self.auth, context.pool(), context.secret()).await?;
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
 
     let reason = self.reason.trim();
-    check_report_reason(reason, context)?;
+    check_report_reason(reason, &local_site)?;
 
     let person_id = local_user_view.person.id;
     let private_message_id = self.private_message_id;
index b5754c9badfbcb853a0f31edf61af727c2a4de81..9ba48f65343a44a7853dbe72126d7bfc2a5d1a66 100644 (file)
@@ -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,
     })
index 56cc9fdf41ee1c019f1f2dee09ec320deab40653..6241c509764628a82424a813678800753ddf44ed 100644 (file)
@@ -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
index 5ebf812d98a33cd78ee399029dee24b261280c16..129e92b7e6488ab42b2ba1205e544eb7b8d9d55f 100644 (file)
@@ -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| {
index ea8f60775283ce19ce41877478a007ae32241338..9329151b3e9a3ccfd8dc3116dd625d896315b486 100644 (file)
@@ -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;
index 0fe2934cebc66c6054593f0bb114f28342a89641..32db67bbe342d6fcf913179b37c89ebdbd3fe9c1 100644 (file)
@@ -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)
index 57e2a5cea9e3f3299a18a4d751b98c4d62db01c1..5d322eeceaf8b40834b1bc8075b2838c295d4f1d 100644 (file)
@@ -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
index 9fcde746ec297229e6a17a9841a2fd33f09c5222..6c02f9ac9e689a5898226a58c5a5208da56dd5ae 100644 (file)
@@ -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);
index e7f6c07c3f14942a0af43a7796b8e21ada92577c..406517cd8e8ae353f371349e66dd57d41609caf3 100644 (file)
@@ -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 }
index 73c11762854da98c0ae3a9b12fac87433a285f46..88e1c3ac8d37c64b540966c03fed3fa9b98fc81f 100644 (file)
@@ -128,6 +128,30 @@ pub struct CreateSite {
   pub application_email_admins: Option<bool>,
   pub auth: Sensitive<String>,
   pub hide_modlog_mod_names: Option<bool>,
+  pub legal_information: Option<String>,
+  pub slur_filter_regex: Option<String>,
+  pub actor_name_max_length: Option<i32>,
+  pub rate_limit_message: Option<i32>,
+  pub rate_limit_message_per_second: Option<i32>,
+  pub rate_limit_post: Option<i32>,
+  pub rate_limit_post_per_second: Option<i32>,
+  pub rate_limit_register: Option<i32>,
+  pub rate_limit_register_per_second: Option<i32>,
+  pub rate_limit_image: Option<i32>,
+  pub rate_limit_image_per_second: Option<i32>,
+  pub rate_limit_comment: Option<i32>,
+  pub rate_limit_comment_per_second: Option<i32>,
+  pub rate_limit_search: Option<i32>,
+  pub rate_limit_search_per_second: Option<i32>,
+  pub federation_enabled: Option<bool>,
+  pub federation_debug: Option<bool>,
+  pub federation_strict_allowlist: Option<bool>,
+  pub federation_http_fetch_retry_limit: Option<i32>,
+  pub federation_worker_count: Option<i32>,
+  pub captcha_enabled: Option<bool>,
+  pub captcha_difficulty: Option<String>,
+  pub allowed_instances: Option<Vec<String>>,
+  pub blocked_instances: Option<Vec<String>>,
 }
 
 #[derive(Debug, Serialize, Deserialize, Clone, Default)]
@@ -151,6 +175,29 @@ pub struct EditSite {
   pub application_email_admins: Option<bool>,
   pub hide_modlog_mod_names: Option<bool>,
   pub discussion_languages: Option<Vec<LanguageId>>,
+  pub slur_filter_regex: Option<String>,
+  pub actor_name_max_length: Option<i32>,
+  pub rate_limit_message: Option<i32>,
+  pub rate_limit_message_per_second: Option<i32>,
+  pub rate_limit_post: Option<i32>,
+  pub rate_limit_post_per_second: Option<i32>,
+  pub rate_limit_register: Option<i32>,
+  pub rate_limit_register_per_second: Option<i32>,
+  pub rate_limit_image: Option<i32>,
+  pub rate_limit_image_per_second: Option<i32>,
+  pub rate_limit_comment: Option<i32>,
+  pub rate_limit_comment_per_second: Option<i32>,
+  pub rate_limit_search: Option<i32>,
+  pub rate_limit_search_per_second: Option<i32>,
+  pub federation_enabled: Option<bool>,
+  pub federation_debug: Option<bool>,
+  pub federation_strict_allowlist: Option<bool>,
+  pub federation_http_fetch_retry_limit: Option<i32>,
+  pub federation_worker_count: Option<i32>,
+  pub captcha_enabled: Option<bool>,
+  pub captcha_difficulty: Option<String>,
+  pub allowed_instances: Option<Vec<String>>,
+  pub blocked_instances: Option<Vec<String>>,
   pub auth: Sensitive<String>,
 }
 
@@ -166,7 +213,7 @@ pub struct SiteResponse {
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
 pub struct GetSiteResponse {
-  pub site_view: Option<SiteView>, // Because the site might not be set up yet
+  pub site_view: SiteView,
   pub admins: Vec<PersonViewSafe>,
   pub online: usize,
   pub version: String,
index 2a02b9a71d16c185d6cd2d468cf167d17e6c997e..6b0b011a9ec6054c43864d8aed3db16a2d0ca4b8 100644 (file)
@@ -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<LocalUserView>,
-  pool: &DbPool,
+  local_site: &LocalSite,
 ) -> Result<(), LemmyError> {
-  if local_user_view.is_none() {
-    let site = blocking(pool, Site::read_local).await?;
-
-    // The site might not be set up yet
-    if let Ok(site) = site {
-      if site.private_instance {
-        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<Option<FederatedInstances>, LemmyError> {
-  let federation_config = &settings.federation;
-  let hostname = &settings.hostname;
-  let federation = federation_config.to_owned();
-  if federation.enabled {
-    let distinct_communities = blocking(pool, move |conn| {
-      Community::distinct_federated_communities(conn)
-    })
-    .await??;
-
-    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::<Result<Vec<String>, LemmyError>>()?;
-
-    if let Some(allowed) = allowed.as_ref() {
-      linked.extend_from_slice(allowed);
-    }
-
-    if let Some(blocked) = blocked.as_ref() {
-      linked.retain(|a| !blocked.contains(a) && !a.eq(hostname));
-    }
-
-    // Sort and remove dupes
-    linked.sort_unstable();
-    linked.dedup();
+    // These can return empty vectors, so convert them to options
+    let allowed = (!allowed.is_empty()).then(|| allowed);
+    let blocked = (!blocked.is_empty()).then(|| blocked);
 
     Ok(Some(FederatedInstances {
       linked,
@@ -467,6 +442,37 @@ fn lang_str_to_lang(lang: &str) -> Lang {
   })
 }
 
+pub fn local_site_rate_limit_to_rate_limit_config(
+  local_site_rate_limit: &LocalSiteRateLimit,
+) -> RateLimitConfig {
+  let l = local_site_rate_limit;
+  RateLimitConfig {
+    message: l.message,
+    message_per_second: l.message_per_second,
+    post: l.post,
+    post_per_second: l.post_per_second,
+    register: l.register,
+    register_per_second: l.register_per_second,
+    image: l.image,
+    image_per_second: l.image_per_second,
+    comment: l.comment,
+    comment_per_second: l.comment_per_second,
+    search: l.search,
+    search_per_second: l.search_per_second,
+  }
+}
+
+pub fn local_site_to_slur_regex(local_site: &LocalSite) -> Option<Regex> {
+  build_slur_regex(local_site.slur_filter_regex.as_deref())
+}
+
+pub fn local_site_opt_to_slur_regex(local_site: &Option<LocalSite>) -> Option<Regex> {
+  local_site
+    .as_ref()
+    .map(local_site_to_slur_regex)
+    .unwrap_or(None)
+}
+
 pub fn send_application_approved_email(
   user: &LocalUserView,
   settings: &Settings,
@@ -506,11 +512,11 @@ pub async fn send_new_applicant_email_to_admins(
 }
 
 pub async fn check_registration_application(
-  site: &Site,
   local_user_view: &LocalUserView,
+  local_site: &LocalSite,
   pool: &DbPool,
 ) -> Result<(), LemmyError> {
-  if site.require_application
+  if local_site.require_application
     && !local_user_view.local_user.accepted_application
     && !local_user_view.person.admin
   {
@@ -531,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<ListingType>,
-  pool: &DbPool,
+  local_site: &LocalSite,
 ) -> Result<ListingType, LemmyError> {
-  Ok(match listing_type {
-    Some(l) => l,
-    None => {
-      let site = blocking(pool, Site::read_local).await??;
-      ListingType::from_str(&site.default_post_listing_type)?
-    }
-  })
+  Ok(listing_type.unwrap_or(ListingType::from_str(
+    &local_site.default_post_listing_type,
+  )?))
 }
index f3effaad6cf9d1e3bbc009dabbf0705b13ed5dbc..ef13e54d9421b2224dd93f6da1da3dd9ec1c68f0 100644 (file)
@@ -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"))?;
index a674ed2c3a82170d7d62682cdb6c1ccb1af0f49f..407d11cceb462b08ee96c6fec40947f21a78ae64 100644 (file)
@@ -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"))?;
index 92e1771c99cf1deb26ec7c74e7b8da4498da409d..9ab7d6d0247d373abf2d2a664387d1e40c9554ca 100644 (file)
@@ -11,7 +11,7 @@ use lemmy_api_common::{
 };
 use lemmy_apub::{fetcher::resolve_actor_identifier, objects::community::ApubCommunity};
 use lemmy_db_schema::{
-  source::{comment::Comment, community::Community},
+  source::{comment::Comment, community::Community, local_site::LocalSite},
   traits::{Crud, DeleteableOrRemoveable},
 };
 use lemmy_db_views::comment_view::CommentQuery;
@@ -32,10 +32,11 @@ impl PerformCrud for GetComments {
     let local_user_view =
       get_local_user_view_from_jwt_opt(data.auth.as_ref(), context.pool(), context.secret())
         .await?;
-    check_private_instance(&local_user_view, context.pool()).await?;
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
+    check_private_instance(&local_user_view, &local_site)?;
 
     let community_id = data.community_id;
-    let listing_type = listing_type_with_site_default(data.type_, context.pool()).await?;
+    let listing_type = listing_type_with_site_default(data.type_, &local_site)?;
 
     let community_actor_id = if let Some(name) = &data.community_name {
       resolve_actor_identifier::<ApubCommunity, Community>(name, context, true)
index f5013c9882c148e29b8bce2bba98247e855b449c..a4868c0977a946fdf8771c43551293820c314197 100644 (file)
@@ -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;
index fe664d80f7cc3e325203149c22e83d8dcb09c4f6..3b94fd474302fe18202e779038e4b8cb55d71dfd 100644 (file)
@@ -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"))?;
index f03ad5f50b5d6d3a3ca19a376f6bd9294665ffd5..f9a18fb3063c911bf601162f9f47765d4ff6aa53 100644 (file)
@@ -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)
     })
index 1f820b9ea03c04bc37f3e6ec5be1af93ad9c80b4..63cdc3afa0dc6954078125e95ea940678906f6dd 100644 (file)
@@ -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)
index 441f45811c29c3709190dec20fa1870e1a1b29fc..dff00696043e61859abcc0b609e1944064a0dfb7 100644 (file)
@@ -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"))?;
index 1aa4585f162fa1f31e1c338921e825e99217ad0b..dcf5886aea1f0ca2c509513aa166b1f6f3a94ea3 100644 (file)
@@ -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);
 
index 3ea32759b263863e286be61e64cf75cdca569a62..595f3e2b9dd640ca755c27fe043a67363bfb6b30 100644 (file)
@@ -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);
 
index 5895a6cbc7e69ae6a2a5f2293e8997614b3f9834..be486144f1bddd9fc935d9499a90acd8f6ed6803 100644 (file)
@@ -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"))?;
index b7872b38f2de69f16f8797a230c607b9a8ba79d7..82ae4cc8fb1c411d7366ec65f17b922b989a1aa7 100644 (file)
@@ -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| {
index 77a3266067710b67fd6160b3a3a3726a705165b9..2c7f605bd21907553fcfc30821585621181b6acb 100644 (file)
@@ -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"))?;
index 734a0246131f904ec03bf28a9dac69587df7c209..78e2f49a2fcffd03e4cdae1de046742f3da7f404 100644 (file)
@@ -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??;
 
index 647d6b2948c7306495e48084da0db66e0d684685..f7f7b2cc2fc56e97be534dc2e3cfd8dd5f34681e 100644 (file)
@@ -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;
index a59e8197b45cb037e27223d833ce65cf5a0d7fec..746b847022f97ba35e5dbb4c1c78428c232fc73f 100644 (file)
@@ -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);
 
index 9ba29158ba862a7cfaf8d8b26b615f4d1211f6fa..d646a008241928ceae574d98ead0870c4dfe8660 100644 (file)
@@ -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??;
 
index 24cb5f0854e1f5cdf8d7fce87027e4f2aedb0071..ab9993e6bc2722329f5b38eca039d53fee9fcaad 100644 (file)
@@ -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| {
index 278031311ed938504c0d1a82731bb687e8e71735..9eeea66d8e98ca9edff620d5772834af60517d56 100644 (file)
@@ -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(),
         )?)
       },
     )
index e7d6702022b826201a54e1e5171fb14f65f4b4e7..8a348223e836e8b964e3015f26e1441c2c576f02 100644 (file)
@@ -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"))?;
index 9de33a69ac6ff65d6b89c37937648d4af0f2af72..9782b64c2d5589f8399a1d8bcecb589630151f3e 100644 (file)
@@ -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"))?;
index 8bcdda4617ab96553e2a84886719ca4fce8a1eb5..5340c657a23b353ab3320872e94111ca773d7af8 100644 (file)
@@ -3,19 +3,29 @@ use activitypub_federation::core::signatures::generate_actor_keypair;
 use actix_web::web::Data;
 use lemmy_api_common::{
   site::{CreateSite, SiteResponse},
-  utils::{blocking, get_local_user_view_from_jwt, is_admin, site_description_length_check},
+  utils::{
+    blocking,
+    get_local_user_view_from_jwt,
+    is_admin,
+    local_site_to_slur_regex,
+    site_description_length_check,
+  },
 };
 use lemmy_apub::generate_site_inbox_url;
 use lemmy_db_schema::{
   newtypes::DbUrl,
-  source::site::{Site, SiteForm},
+  source::{
+    local_site::{LocalSite, LocalSiteUpdateForm},
+    local_site_rate_limit::{LocalSiteRateLimit, LocalSiteRateLimitUpdateForm},
+    site::{Site, SiteUpdateForm},
+  },
   traits::Crud,
   utils::{diesel_option_overwrite, diesel_option_overwrite_to_url, naive_now},
 };
 use lemmy_db_views::structs::SiteView;
 use lemmy_utils::{
   error::LemmyError,
-  utils::{check_slurs, check_slurs_opt},
+  utils::{check_application_question, check_slurs, check_slurs_opt},
   ConnectionId,
 };
 use lemmy_websocket::LemmyContext;
@@ -33,8 +43,9 @@ impl PerformCrud for CreateSite {
   ) -> Result<SiteResponse, LemmyError> {
     let data: &CreateSite = self;
 
-    let read_site = Site::read_local;
-    if blocking(context.pool(), read_site).await?.is_ok() {
+    let local_site = blocking(context.pool(), LocalSite::read).await??;
+
+    if local_site.site_setup {
       return Err(LemmyError::from_message("site_already_exists"));
     };
 
@@ -46,8 +57,9 @@ impl PerformCrud for CreateSite {
     let icon = diesel_option_overwrite_to_url(&data.icon)?;
     let banner = diesel_option_overwrite_to_url(&data.banner)?;
 
-    check_slurs(&data.name, &context.settings().slur_regex())?;
-    check_slurs_opt(&data.description, &context.settings().slur_regex())?;
+    let slur_regex = local_site_to_slur_regex(&local_site);
+    check_slurs(&data.name, &slur_regex)?;
+    check_slurs_opt(&data.description, &slur_regex)?;
 
     // Make sure user is an admin
     is_admin(&local_user_view)?;
@@ -56,35 +68,82 @@ impl PerformCrud for CreateSite {
       site_description_length_check(desc)?;
     }
 
+    let application_question = diesel_option_overwrite(&data.application_question);
+    check_application_question(&application_question, &data.require_application)?;
+
     let actor_id: DbUrl = Url::parse(&context.settings().get_protocol_and_hostname())?.into();
     let inbox_url = Some(generate_site_inbox_url(&actor_id)?);
     let keypair = generate_actor_keypair()?;
-    let site_form = SiteForm {
-      name: data.name.to_owned(),
-      sidebar,
-      description,
-      icon,
-      banner,
-      enable_downvotes: data.enable_downvotes,
-      open_registration: data.open_registration,
-      enable_nsfw: data.enable_nsfw,
-      community_creation_admin_only: data.community_creation_admin_only,
-      actor_id: Some(actor_id),
-      last_refreshed_at: Some(naive_now()),
-      inbox_url,
-      private_key: Some(Some(keypair.private_key)),
-      public_key: Some(keypair.public_key),
-      default_theme: data.default_theme.clone(),
-      default_post_listing_type: data.default_post_listing_type.clone(),
-      application_email_admins: data.application_email_admins,
-      hide_modlog_mod_names: data.hide_modlog_mod_names,
-      ..SiteForm::default()
-    };
+    let site_form = SiteUpdateForm::builder()
+      .name(Some(data.name.to_owned()))
+      .sidebar(sidebar)
+      .description(description)
+      .icon(icon)
+      .banner(banner)
+      .actor_id(Some(actor_id))
+      .last_refreshed_at(Some(naive_now()))
+      .inbox_url(inbox_url)
+      .private_key(Some(Some(keypair.private_key)))
+      .public_key(Some(keypair.public_key))
+      .build();
+
+    let 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??;
 
index fc3293a7dadf9e8689bc58ea293a84d7593595b3..226e24f341a25567c3aca1170bda217a7c862774 100644 (file)
@@ -1,8 +1,7 @@
 use crate::PerformCrud;
 use actix_web::web::Data;
 use lemmy_api_common::{
-  person::Register,
-  site::{CreateSite, GetSite, GetSiteResponse, MyUserInfo},
+  site::{GetSite, GetSiteResponse, MyUserInfo},
   utils::{blocking, build_federated_instances, get_local_user_settings_view_from_jwt_opt},
 };
 use lemmy_db_schema::source::{actor_language::SiteLanguage, language::Language};
@@ -16,56 +15,20 @@ use lemmy_db_views_actor::structs::{
 };
 use lemmy_utils::{error::LemmyError, version, ConnectionId};
 use lemmy_websocket::{messages::GetUsersOnline, LemmyContext};
-use tracing::info;
 
 #[async_trait::async_trait(?Send)]
 impl PerformCrud for GetSite {
   type Response = GetSiteResponse;
 
-  #[tracing::instrument(skip(context, websocket_id))]
+  #[tracing::instrument(skip(context, _websocket_id))]
   async fn perform(
     &self,
     context: &Data<LemmyContext>,
-    websocket_id: Option<ConnectionId>,
+    _websocket_id: Option<ConnectionId>,
   ) -> Result<GetSiteResponse, LemmyError> {
     let data: &GetSite = self;
 
-    let site_view = match blocking(context.pool(), SiteView::read_local).await? {
-      Ok(site_view) => Some(site_view),
-      // If the site isn't created yet, check the setup
-      Err(_) => {
-        if let Some(setup) = context.settings().setup.as_ref() {
-          let register = Register {
-            username: setup.admin_username.to_owned(),
-            email: setup.admin_email.clone().map(|s| s.into()),
-            password: setup.admin_password.clone().into(),
-            password_verify: setup.admin_password.clone().into(),
-            show_nsfw: true,
-            captcha_uuid: None,
-            captcha_answer: None,
-            honeypot: None,
-            answer: None,
-          };
-          let admin_jwt = register
-            .perform(context, websocket_id)
-            .await?
-            .jwt
-            .expect("jwt is returned from registration on newly created site");
-          info!("Admin {} created", setup.admin_username);
-
-          let create_site = CreateSite {
-            name: setup.site_name.to_owned(),
-            auth: admin_jwt,
-            ..CreateSite::default()
-          };
-          create_site.perform(context, websocket_id).await?;
-          info!("Site {} created", setup.site_name);
-          Some(blocking(context.pool(), SiteView::read_local).await??)
-        } else {
-          None
-        }
-      }
-    };
+    let site_view = blocking(context.pool(), SiteView::read_local).await??;
 
     let admins = blocking(context.pool(), PersonViewSafe::admins).await??;
 
@@ -130,7 +93,8 @@ impl PerformCrud for GetSite {
       None
     };
 
-    let federated_instances = build_federated_instances(context.pool(), context.settings()).await?;
+    let federated_instances =
+      build_federated_instances(&site_view.local_site, context.pool()).await?;
 
     let all_languages = blocking(context.pool(), Language::read_all).await??;
     let discussion_languages = blocking(context.pool(), SiteLanguage::read_local).await??;
index 788546eac2d22893287386b431146f851c34959d..47915e43451aa78c9aeb8b9e8c0b49a4e4f2619f 100644 (file)
@@ -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)
       })
index 6560783b78655e1d95fe6dac80901e5d6ac96a40..88bc43d06e9a5628e3dd9cfa7b4d8fcb778dcd21 100644 (file)
@@ -6,6 +6,7 @@ use lemmy_api_common::{
   utils::{
     blocking,
     honeypot_check,
+    local_site_to_slur_regex,
     password_length_check,
     send_new_applicant_email_to_admins,
     send_verification_email,
@@ -20,14 +21,13 @@ use lemmy_apub::{
 use lemmy_db_schema::{
   aggregates::structs::PersonAggregates,
   source::{
-    local_user::{LocalUser, LocalUserForm},
-    person::{Person, PersonForm},
-    registration_application::{RegistrationApplication, RegistrationApplicationForm},
-    site::Site,
+    local_user::{LocalUser, LocalUserInsertForm},
+    person::{Person, PersonInsertForm},
+    registration_application::{RegistrationApplication, RegistrationApplicationInsertForm},
   },
   traits::Crud,
 };
-use lemmy_db_views::structs::LocalUserView;
+use lemmy_db_views::structs::{LocalUserView, SiteView};
 use lemmy_db_views_actor::structs::PersonViewSafe;
 use lemmy_utils::{
   claims::Claims,
@@ -49,27 +49,21 @@ impl PerformCrud for Register {
   ) -> Result<LoginResponse, LemmyError> {
     let data: &Register = self;
 
-    // no email verification, or applications if the site is not setup yet
-    let (mut email_verification, mut require_application) = (false, false);
+    let site_view = blocking(context.pool(), SiteView::read_local).await??;
+    let local_site = site_view.local_site;
 
-    // Make sure site has open registration
-    let site = blocking(context.pool(), Site::read_local).await?;
-    if let Ok(site) = &site {
-      if !site.open_registration {
-        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;
       }
     }
index e77a4b3f3612fbb03d6ac1234ee866ea7ddec125..5e6355ce4cd1186cab3e6019d8410e71aadd2191 100644 (file)
@@ -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,
index 72c4316373aa77706714f7ab93f5ffc8a5dc53e8..3e574232d24e1fc0f770720dfc748d2cd5ba4cc4 100644 (file)
@@ -9,6 +9,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
   protocol::activities::block::block_user::BlockUser,
@@ -33,7 +35,7 @@ use lemmy_db_schema::{
       CommunityPersonBanForm,
     },
     moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
-    person::Person,
+    person::{Person, PersonUpdateForm},
   },
   traits::{Bannable, Crud, Followable},
 };
@@ -123,6 +125,10 @@ impl ActivityHandler for BlockUser {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     match self
       .target
@@ -177,7 +183,14 @@ impl ActivityHandler for BlockUser {
     match target {
       SiteOrCommunity::Site(_site) => {
         let blocked_person = blocking(context.pool(), move |conn| {
-          Person::ban_person(conn, blocked_person.id, true, expires)
+          Person::update(
+            conn,
+            blocked_person.id,
+            &PersonUpdateForm::builder()
+              .banned(Some(true))
+              .ban_expires(Some(expires))
+              .build(),
+          )
         })
         .await??;
         if self.remove_data.unwrap_or(false) {
index ee6f631aee0a137747898850bce7d8b01a0fae21..b93d0aa3a77b9bc2d7e0ffadcbe9f5f425ba72e0 100644 (file)
@@ -7,6 +7,8 @@ use crate::{
     verify_is_public,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, instance::remote_instance_inboxes, person::ApubPerson},
   protocol::activities::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
@@ -24,7 +26,7 @@ use lemmy_db_schema::{
   source::{
     community::{CommunityPersonBan, CommunityPersonBanForm},
     moderator::{ModBan, ModBanForm, ModBanFromCommunity, ModBanFromCommunityForm},
-    person::Person,
+    person::{Person, PersonUpdateForm},
   },
   traits::{Bannable, Crud},
 };
@@ -90,6 +92,10 @@ impl ActivityHandler for UndoBlockUser {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     verify_domains_match(self.actor.inner(), self.object.actor.inner())?;
     self.object.verify(context, request_counter).await?;
@@ -121,7 +127,14 @@ impl ActivityHandler for UndoBlockUser {
     {
       SiteOrCommunity::Site(_site) => {
         let blocked_person = blocking(context.pool(), move |conn| {
-          Person::ban_person(conn, blocked_person.id, false, expires)
+          Person::update(
+            conn,
+            blocked_person.id,
+            &PersonUpdateForm::builder()
+              .banned(Some(false))
+              .ban_expires(Some(expires))
+              .build(),
+          )
         })
         .await??;
 
index 4dbd463b70463596d6f7b5e2f55aaf33540d3f3f..86ee916661c267398b2fb1a2a9f48699dfae8bad 100644 (file)
@@ -12,6 +12,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   generate_moderators_url,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
@@ -84,6 +86,10 @@ impl ActivityHandler for AddMod {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
index e5419680ef9fda18266e121d34b4cc5a90c008a9..2886c2bf0ff2d81e0c266914b98d2ee8216a8ebe 100644 (file)
@@ -1,6 +1,8 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_is_public},
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   insert_activity,
   objects::community::ApubCommunity,
   protocol::{
@@ -11,6 +13,7 @@ use crate::{
 };
 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
 use activitystreams_kinds::{activity::AnnounceType, public};
+use lemmy_api_common::utils::blocking;
 use lemmy_utils::error::LemmyError;
 use lemmy_websocket::LemmyContext;
 use tracing::debug;
@@ -84,9 +87,13 @@ impl ActivityHandler for AnnounceActivity {
   #[tracing::instrument(skip_all)]
   async fn verify(
     &self,
-    _context: &Data<LemmyContext>,
+    context: &Data<LemmyContext>,
     _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     Ok(())
   }
index ede6a0089cb9b22eca63714e2ab41d3d266b7536..3dca02397a719447fb74724951237b39218e1e27 100644 (file)
@@ -12,6 +12,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   generate_moderators_url,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
@@ -84,6 +86,10 @@ impl ActivityHandler for RemoveMod {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
index c3b50abfeeb915d18cf07cf3abf125c34527d364..de3fb3566d1ca0357ad8b8bdef9fad9da158e703 100644 (file)
@@ -1,5 +1,7 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_person_in_community},
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::community::report::Report,
@@ -74,6 +76,10 @@ impl ActivityHandler for Report {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     let community = self.to[0]
       .dereference(context, local_instance(context), request_counter)
       .await?;
index 57a31221e45ad2da169f3710c21842b13fdb48be..b444f4f5eb6e49e1418a26fa07aa9a08a959d907 100644 (file)
@@ -7,6 +7,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::community::update::UpdateCommunity,
@@ -19,10 +21,7 @@ use activitypub_federation::{
 };
 use activitystreams_kinds::{activity::UpdateType, public};
 use lemmy_api_common::utils::blocking;
-use lemmy_db_schema::{
-  source::community::{Community, CommunityForm},
-  traits::Crud,
-};
+use lemmy_db_schema::{source::community::Community, traits::Crud};
 use lemmy_utils::error::LemmyError;
 use lemmy_websocket::{send::send_community_ws_message, LemmyContext, UserOperationCrud};
 use url::Url;
@@ -72,6 +71,9 @@ impl ActivityHandler for UpdateCommunity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_is_public(&self.to, &self.cc)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
@@ -101,19 +103,10 @@ impl ActivityHandler for UpdateCommunity {
   ) -> Result<(), LemmyError> {
     let community = self.get_community(context, request_counter).await?;
 
-    let updated_community = self.object.into_form();
-    let cf = CommunityForm {
-      name: updated_community.name,
-      title: updated_community.title,
-      description: updated_community.description,
-      nsfw: updated_community.nsfw,
-      // TODO: icon and banner would be hosted on the other instance, ideally we would copy it to ours
-      icon: updated_community.icon,
-      banner: updated_community.banner,
-      ..CommunityForm::default()
-    };
+    let community_update_form = self.object.into_update_form();
+
     let updated_community = blocking(context.pool(), move |conn| {
-      Community::update(conn, community.id, &cf)
+      Community::update(conn, community.id, &community_update_form)
     })
     .await??;
 
index dad6ada9bf96e5ddcfeead994ef646ef35c56347..3e0de366ff899ef87ddf1b626532c71d70d86513 100644 (file)
@@ -8,6 +8,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   mentions::MentionOrValue,
   objects::{comment::ApubComment, community::ApubCommunity, person::ApubPerson},
@@ -115,6 +117,10 @@ impl ActivityHandler for CreateOrUpdateComment {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_is_public(&self.to, &self.cc)?;
     let post = self.object.get_parents(context, request_counter).await?.0;
     let community = self.get_community(context, request_counter).await?;
index d9d7b854549ffd79d5f3ae5415f2f7c8879e7c71..1f997f72f458442b24ef5964f311040a5f65e4a5 100644 (file)
@@ -8,6 +8,8 @@ use crate::{
     verify_person_in_community,
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   objects::{community::ApubCommunity, person::ApubPerson, post::ApubPost},
   protocol::activities::{create_or_update::post::CreateOrUpdatePost, CreateOrUpdateType},
   ActorType,
@@ -93,6 +95,9 @@ impl ActivityHandler for CreateOrUpdatePost {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_is_public(&self.to, &self.cc)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
index 9ad54769121d9cd6009254efb43abb6552746d77..20310bc4114a089a3b0c37be3a1de3aedf7113de 100644 (file)
@@ -1,5 +1,7 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_person},
+  check_apub_id_valid,
+  fetch_local_site_data,
   objects::{person::ApubPerson, private_message::ApubPrivateMessage},
   protocol::activities::{
     create_or_update::private_message::CreateOrUpdatePrivateMessage,
@@ -69,6 +71,10 @@ impl ActivityHandler for CreateOrUpdatePrivateMessage {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_person(&self.actor, context, request_counter).await?;
     verify_domains_match(self.actor.inner(), self.object.id.inner())?;
     verify_domains_match(self.to[0].inner(), self.object.to[0].inner())?;
index d2572590ad7a4691426e05671e3c678234933416..95024c475c3c1b3ac609c03e9b6320d33bbcbb4f 100644 (file)
@@ -4,6 +4,8 @@ use crate::{
     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
     generate_activity_id,
   },
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::{activities::deletion::delete::Delete, IdOrNestedObject},
@@ -14,8 +16,8 @@ use anyhow::anyhow;
 use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
   source::{
-    comment::Comment,
-    community::Community,
+    comment::{Comment, CommentUpdateForm},
+    community::{Community, CommunityUpdateForm},
     moderator::{
       ModRemoveComment,
       ModRemoveCommentForm,
@@ -24,7 +26,7 @@ use lemmy_db_schema::{
       ModRemovePost,
       ModRemovePostForm,
     },
-    post::Post,
+    post::{Post, PostUpdateForm},
   },
   traits::Crud,
 };
@@ -55,6 +57,9 @@ impl ActivityHandler for Delete {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_delete_activity(self, self.summary.is_some(), context, request_counter).await?;
     Ok(())
   }
@@ -150,7 +155,11 @@ pub(in crate::activities) async fn receive_remove_action(
       })
       .await??;
       let deleted_community = blocking(context.pool(), move |conn| {
-        Community::update_removed(conn, community.id, true)
+        Community::update(
+          conn,
+          community.id,
+          &CommunityUpdateForm::builder().removed(Some(true)).build(),
+        )
       })
       .await??;
 
@@ -168,7 +177,11 @@ pub(in crate::activities) async fn receive_remove_action(
       })
       .await??;
       let removed_post = blocking(context.pool(), move |conn| {
-        Post::update_removed(conn, post.id, true)
+        Post::update(
+          conn,
+          post.id,
+          &PostUpdateForm::builder().removed(Some(true)).build(),
+        )
       })
       .await??;
 
@@ -186,7 +199,11 @@ pub(in crate::activities) async fn receive_remove_action(
       })
       .await??;
       let removed_comment = blocking(context.pool(), move |conn| {
-        Comment::update_removed(conn, comment.id, true)
+        Comment::update(
+          conn,
+          comment.id,
+          &CommentUpdateForm::builder().removed(Some(true)).build(),
+        )
       })
       .await??;
 
index 9570d85d2199e36d495ece4f0fd53f390553fba7..5fb453eef38d87c515f7888224030e72319254cb 100644 (file)
@@ -1,5 +1,7 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_is_public, verify_person},
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{instance::remote_instance_inboxes, person::ApubPerson},
   protocol::activities::deletion::delete_user::DeleteUser,
@@ -11,7 +13,7 @@ use activitypub_federation::{
   utils::verify_urls_match,
 };
 use activitystreams_kinds::{activity::DeleteType, public};
-use lemmy_api_common::utils::delete_user_account;
+use lemmy_api_common::utils::{blocking, delete_user_account};
 use lemmy_utils::error::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
@@ -36,6 +38,9 @@ impl ActivityHandler for DeleteUser {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_is_public(&self.to, &[])?;
     verify_person(&self.actor, context, request_counter).await?;
     verify_urls_match(self.actor.inner(), self.object.inner())?;
index 87dcd2d8f83a44db2ea268996e46688daf09ca93..0a131c40143fae2be4094723680b7250f3d875a9 100644 (file)
@@ -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??;
 
index c43e4d87194c47a05c6b61663acf2be4fa81942f..f73c780c2de38670340d54a61f00fac5fe860786 100644 (file)
@@ -4,6 +4,8 @@ use crate::{
     deletion::{receive_delete_action, verify_delete_activity, DeletableObjects},
     generate_activity_id,
   },
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::deletion::{delete::Delete, undo_delete::UndoDelete},
@@ -13,8 +15,8 @@ use activitystreams_kinds::activity::UndoType;
 use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
   source::{
-    comment::Comment,
-    community::Community,
+    comment::{Comment, CommentUpdateForm},
+    community::{Community, CommunityUpdateForm},
     moderator::{
       ModRemoveComment,
       ModRemoveCommentForm,
@@ -23,7 +25,7 @@ use lemmy_db_schema::{
       ModRemovePost,
       ModRemovePostForm,
     },
-    post::Post,
+    post::{Post, PostUpdateForm},
   },
   traits::Crud,
 };
@@ -54,6 +56,9 @@ impl ActivityHandler for UndoDelete {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     self.object.verify(context, request_counter).await?;
     verify_delete_activity(
       &self.object,
@@ -148,7 +153,11 @@ impl UndoDelete {
         })
         .await??;
         let deleted_community = blocking(context.pool(), move |conn| {
-          Community::update_removed(conn, community.id, false)
+          Community::update(
+            conn,
+            community.id,
+            &CommunityUpdateForm::builder().removed(Some(false)).build(),
+          )
         })
         .await??;
         send_community_ws_message(deleted_community.id, EditCommunity, None, None, context).await?;
@@ -165,7 +174,11 @@ impl UndoDelete {
         })
         .await??;
         let removed_post = blocking(context.pool(), move |conn| {
-          Post::update_removed(conn, post.id, false)
+          Post::update(
+            conn,
+            post.id,
+            &PostUpdateForm::builder().removed(Some(false)).build(),
+          )
         })
         .await??;
         send_post_ws_message(removed_post.id, EditPost, None, None, context).await?;
@@ -182,7 +195,11 @@ impl UndoDelete {
         })
         .await??;
         let removed_comment = blocking(context.pool(), move |conn| {
-          Comment::update_removed(conn, comment.id, false)
+          Comment::update(
+            conn,
+            comment.id,
+            &CommentUpdateForm::builder().removed(Some(false)).build(),
+          )
         })
         .await??;
         send_comment_ws_message_simple(removed_comment.id, EditComment, context).await?;
index f964da08673a57e311d3aacf54fdd0f7a730fa2d..c4bbbb1bb6596aa4d65a3319740cf3daea9d7d3f 100644 (file)
@@ -1,5 +1,7 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity},
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
   ActorType,
@@ -67,6 +69,10 @@ impl ActivityHandler for AcceptFollowCommunity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
+
     verify_urls_match(self.actor.inner(), self.object.object.inner())?;
     self.object.verify(context, request_counter).await?;
     Ok(())
index b0ae72c7168088ba024936a2e5b067865f4fd2a3..512c074cdc068472a975fc880c994114c80958d1 100644 (file)
@@ -5,6 +5,8 @@ use crate::{
     verify_person,
     verify_person_in_community,
   },
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::following::{accept::AcceptFollowCommunity, follow::FollowCommunity},
@@ -84,6 +86,9 @@ impl ActivityHandler for FollowCommunity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_person(&self.actor, context, request_counter).await?;
     let community = self
       .object
index 94b0b68ecef5569930fe91b57c12a40c5ed2280c..b37e21fb28cb63883edf95871fd29865393ebaa5 100644 (file)
@@ -1,5 +1,7 @@
 use crate::{
   activities::{generate_activity_id, send_lemmy_activity, verify_person},
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::following::{follow::FollowCommunity, undo_follow::UndoFollowCommunity},
@@ -63,6 +65,9 @@ impl ActivityHandler for UndoFollowCommunity {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
     verify_person(&self.actor, context, request_counter).await?;
     self.object.verify(context, request_counter).await?;
index 8205967f72ddf13e8de4c5a8dc602a946d6ddb39..0c013fbe33a3b16148e6bfc6e24311b5cf1be633 100644 (file)
@@ -14,7 +14,10 @@ use activitypub_federation::{
 use activitystreams_kinds::public;
 use anyhow::anyhow;
 use lemmy_api_common::utils::blocking;
-use lemmy_db_schema::{newtypes::CommunityId, source::community::Community};
+use lemmy_db_schema::{
+  newtypes::CommunityId,
+  source::{community::Community, local_site::LocalSite},
+};
 use lemmy_db_views_actor::structs::{CommunityPersonBanView, CommunityView};
 use lemmy_utils::error::LemmyError;
 use lemmy_websocket::LemmyContext;
@@ -167,9 +170,14 @@ where
   ActorT: Actor + ActorType,
   Activity: ActivityHandler<Error = LemmyError>,
 {
-  if !context.settings().federation.enabled {
+  let federation_enabled = blocking(context.pool(), &LocalSite::read)
+    .await?
+    .map(|l| l.federation_enabled)
+    .unwrap_or(false);
+  if !federation_enabled {
     return Ok(());
   }
+
   info!("Sending activity {}", activity.id().to_string());
   let activity = WithContext::new(activity, CONTEXT.deref().clone());
 
index f6fc36a2de7e8ca8ee00c846698983ce846c847d..80e319bf0a4c560bc48409beb0f7b5384fb9c47b 100644 (file)
@@ -6,6 +6,8 @@ use crate::{
     voting::{undo_vote_comment, undo_vote_post},
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::voting::{
@@ -82,6 +84,9 @@ impl ActivityHandler for UndoVote {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
     verify_urls_match(self.actor.inner(), self.object.actor.inner())?;
index aafde574962d70252aadfcaf13909354bd4ca6ff..7d07fe8cd978cffc485fe51a50d95e7323e21ec5 100644 (file)
@@ -6,6 +6,8 @@ use crate::{
     voting::{vote_comment, vote_post},
   },
   activity_lists::AnnouncableActivities,
+  check_apub_id_valid,
+  fetch_local_site_data,
   local_instance,
   objects::{community::ApubCommunity, person::ApubPerson},
   protocol::activities::voting::vote::{Vote, VoteType},
@@ -17,7 +19,7 @@ use anyhow::anyhow;
 use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
   newtypes::CommunityId,
-  source::{community::Community, post::Post, site::Site},
+  source::{community::Community, local_site::LocalSite, post::Post},
   traits::Crud,
 };
 use lemmy_utils::error::LemmyError;
@@ -81,10 +83,16 @@ impl ActivityHandler for Vote {
     context: &Data<LemmyContext>,
     request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    check_apub_id_valid(self.id(), &local_site_data, context.settings())
+      .map_err(LemmyError::from_message)?;
     let community = self.get_community(context, request_counter).await?;
     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
-    let site = blocking(context.pool(), Site::read_local).await??;
-    if self.kind == VoteType::Dislike && !site.enable_downvotes {
+    let enable_downvotes = blocking(context.pool(), LocalSite::read)
+      .await?
+      .map(|l| l.enable_downvotes)
+      .unwrap_or(true);
+    if self.kind == VoteType::Dislike && !enable_downvotes {
       return Err(anyhow!("Downvotes disabled").into());
     }
     Ok(())
index 9f4374dbb6df3585733c611d76f7d4ba443552ea..3f88010d50bd2fa0ba6883c5bc752cbb84fc46de 100644 (file)
@@ -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();
   }
 }
index 428c8d59a41c8ebffb40b722a989180f8638f3ca..0dfdfc2227accebb9e57e8a96efe9725b7b0ba58 100644 (file)
@@ -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"));
   }
 
index d90bf40029cdc7e18b5feff5101940d00a81c1ce..a588b3127b03295cebabd31bc44ce6a4f22c173c 100644 (file)
@@ -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
index 9d0fde73b146fca19ec7d51b2667c9e86e7a72a2..503bc5a9c572e89de6ec96812fea4c50d16bad8c 100644 (file)
@@ -7,7 +7,7 @@ use crate::{
 use activitypub_federation::{deser::context::WithContext, traits::ApubObject};
 use actix_web::{web, HttpRequest, HttpResponse};
 use lemmy_api_common::utils::blocking;
-use lemmy_db_schema::source::site::Site;
+use lemmy_db_views::structs::SiteView;
 use lemmy_utils::error::LemmyError;
 use lemmy_websocket::LemmyContext;
 use url::Url;
@@ -15,7 +15,10 @@ use url::Url;
 pub(crate) async fn get_apub_site_http(
   context: web::Data<LemmyContext>,
 ) -> Result<HttpResponse, LemmyError> {
-  let site: ApubSite = blocking(context.pool(), Site::read_local).await??.into();
+  let site: ApubSite = blocking(context.pool(), SiteView::read_local)
+    .await??
+    .site
+    .into();
 
   let apub = site.into_apub(&context).await?;
   Ok(create_apub_response(&apub))
index b594acad5b2d6ed83cb685669bf51b4cf5411409..471a7564f5dfc6f5a8b628d26f298085b414ce76 100644 (file)
@@ -6,13 +6,14 @@ use activitypub_federation::{
   LocalInstance,
 };
 use anyhow::Context;
+use diesel::PgConnection;
 use lemmy_api_common::utils::blocking;
-use lemmy_db_schema::{newtypes::DbUrl, source::activity::Activity, utils::DbPool};
-use lemmy_utils::{
-  error::LemmyError,
-  location_info,
-  settings::{structs::Settings, SETTINGS},
+use lemmy_db_schema::{
+  newtypes::DbUrl,
+  source::{activity::Activity, instance::Instance, local_site::LocalSite},
+  utils::DbPool,
 };
+use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
 use lemmy_websocket::LemmyContext;
 use once_cell::sync::{Lazy, OnceCell};
 use url::{ParseError, Url};
@@ -31,16 +32,35 @@ static CONTEXT: Lazy<Vec<serde_json::Value>> = Lazy::new(|| {
 });
 
 // TODO: store this in context? but its only used in this crate, no need to expose it elsewhere
+// TODO this singleton needs to be redone to account for live data.
 fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
   static LOCAL_INSTANCE: OnceCell<LocalInstance> = OnceCell::new();
   LOCAL_INSTANCE.get_or_init(|| {
+    let conn = &mut context
+      .pool()
+      .get()
+      .expect("getting connection for LOCAL_INSTANCE init");
+    // Local site may be missing
+    let local_site = &LocalSite::read(conn);
+    let worker_count = local_site
+      .as_ref()
+      .map(|l| l.federation_worker_count)
+      .unwrap_or(64) as u64;
+    let http_fetch_retry_limit = local_site
+      .as_ref()
+      .map(|l| l.federation_http_fetch_retry_limit)
+      .unwrap_or(25);
+    let federation_debug = local_site
+      .as_ref()
+      .map(|l| l.federation_debug)
+      .unwrap_or(true);
+
     let settings = InstanceSettings::builder()
-      .http_fetch_retry_limit(context.settings().federation.http_fetch_retry_limit)
-      .worker_count(context.settings().federation.worker_count)
-      .debug(context.settings().federation.debug)
+      .http_fetch_retry_limit(http_fetch_retry_limit)
+      .worker_count(worker_count)
+      .debug(federation_debug)
       // TODO No idea why, but you can't pass context.settings() to the verify_url_function closure
       // without the value getting captured.
-      .verify_url_function(|url| check_apub_id_valid(url, &SETTINGS))
       .http_signature_compat(true)
       .build()
       .expect("configure federation");
@@ -62,8 +82,13 @@ fn local_instance(context: &LemmyContext) -> &'static LocalInstance {
 ///
 /// `use_strict_allowlist` should be true only when parsing a remote community, or when parsing a
 /// post/comment in a local community.
-#[tracing::instrument(skip(settings))]
-fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'static str> {
+#[tracing::instrument(skip(settings, local_site_data))]
+// TODO This function needs to be called by incoming activities
+fn check_apub_id_valid(
+  apub_id: &Url,
+  local_site_data: &LocalSiteData,
+  settings: &Settings,
+) -> Result<(), &'static str> {
   let domain = apub_id.domain().expect("apud id has domain").to_string();
   let local_instance = settings
     .get_hostname_without_port()
@@ -72,7 +97,12 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
     return Ok(());
   }
 
-  if !settings.federation.enabled {
+  if !local_site_data
+    .local_site
+    .as_ref()
+    .map(|l| l.federation_enabled)
+    .unwrap_or(true)
+  {
     return Err("Federation disabled");
   }
 
@@ -80,13 +110,13 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
     return Err("Invalid protocol scheme");
   }
 
-  if let Some(blocked) = settings.to_owned().federation.blocked_instances {
+  if let Some(blocked) = local_site_data.blocked_instances.as_ref() {
     if blocked.contains(&domain) {
       return Err("Domain is blocked");
     }
   }
 
-  if let Some(allowed) = settings.to_owned().federation.allowed_instances {
+  if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
     if !allowed.contains(&domain) {
       return Err("Domain is not in allowlist");
     }
@@ -95,13 +125,40 @@ fn check_apub_id_valid(apub_id: &Url, settings: &Settings) -> Result<(), &'stati
   Ok(())
 }
 
-#[tracing::instrument(skip(settings))]
+#[derive(Clone)]
+pub(crate) struct LocalSiteData {
+  local_site: Option<LocalSite>,
+  allowed_instances: Option<Vec<String>>,
+  blocked_instances: Option<Vec<String>>,
+}
+
+pub(crate) fn fetch_local_site_data(
+  conn: &mut PgConnection,
+) -> Result<LocalSiteData, diesel::result::Error> {
+  // LocalSite may be missing
+  let local_site = LocalSite::read(conn).ok();
+  let allowed = Instance::allowlist(conn)?;
+  let blocked = Instance::blocklist(conn)?;
+
+  // These can return empty vectors, so convert them to options
+  let allowed_instances = (!allowed.is_empty()).then(|| allowed);
+  let blocked_instances = (!blocked.is_empty()).then(|| blocked);
+
+  Ok(LocalSiteData {
+    local_site,
+    allowed_instances,
+    blocked_instances,
+  })
+}
+
+#[tracing::instrument(skip(settings, local_site_data))]
 pub(crate) fn check_apub_id_valid_with_strictness(
   apub_id: &Url,
   is_strict: bool,
+  local_site_data: &LocalSiteData,
   settings: &Settings,
 ) -> Result<(), LemmyError> {
-  check_apub_id_valid(apub_id, settings).map_err(LemmyError::from_message)?;
+  check_apub_id_valid(apub_id, local_site_data, settings).map_err(LemmyError::from_message)?;
   let domain = apub_id.domain().expect("apud id has domain").to_string();
   let local_instance = settings
     .get_hostname_without_port()
@@ -110,15 +167,20 @@ pub(crate) fn check_apub_id_valid_with_strictness(
     return Ok(());
   }
 
-  if let Some(mut allowed) = settings.to_owned().federation.allowed_instances {
+  if let Some(allowed) = local_site_data.allowed_instances.as_ref() {
     // Only check allowlist if this is a community, or strict allowlist is enabled.
-    let strict_allowlist = settings.to_owned().federation.strict_allowlist;
+    let strict_allowlist = local_site_data
+      .local_site
+      .as_ref()
+      .map(|l| l.federation_strict_allowlist)
+      .unwrap_or(true);
     if is_strict || strict_allowlist {
       // need to allow this explicitly because apub receive might contain objects from our local
       // instance.
-      allowed.push(local_instance);
+      let mut allowed_and_local = allowed.to_owned();
+      allowed_and_local.push(local_instance);
 
-      if !allowed.contains(&domain) {
+      if !allowed_and_local.contains(&domain) {
         return Err(LemmyError::from_message(
           "Federation forbidden by strict allowlist",
         ));
@@ -203,7 +265,7 @@ async fn insert_activity(
   let ap_id = ap_id.to_owned().into();
   Ok(
     blocking(pool, move |conn| {
-      Activity::insert(conn, ap_id, activity, local, sensitive)
+      Activity::insert(conn, ap_id, activity, local, Some(sensitive))
     })
     .await??,
   )
index e292d2b671ac58b13349d4bf9e703fe1507c8235..154bd41c982e78884667da38c9dc4553389f3569 100644 (file)
@@ -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(
       &note.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(&note.content, &note.media_type, &note.source);
-    let content_slurs_removed = remove_slurs(&content, &context.settings().slur_regex());
+
+    let local_site = blocking(context.pool(), LocalSite::read).await?.ok();
+    let slur_regex = &local_site_opt_to_slur_regex(&local_site);
+    let content_slurs_removed = remove_slurs(&content, slur_regex);
     let language_id = LanguageTag::to_language_id_single(note.language, context.pool()).await?;
 
-    let 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]
index 9793c4fd4ef43f24b281511ad95ad5f34176cadd..6e04f2c1e822cb32b35770a716ae85b118ad5b23 100644 (file)
@@ -1,6 +1,7 @@
 use crate::{
   check_apub_id_valid_with_strictness,
   collections::{community_moderators::ApubCommunityModerators, CommunityContext},
+  fetch_local_site_data,
   generate_moderators_url,
   generate_outbox_url,
   local_instance,
@@ -21,8 +22,12 @@ use chrono::NaiveDateTime;
 use itertools::Itertools;
 use lemmy_api_common::utils::blocking;
 use lemmy_db_schema::{
-  source::{actor_language::CommunityLanguage, community::Community},
-  traits::ApubActor,
+  source::{
+    actor_language::CommunityLanguage,
+    community::{Community, CommunityUpdateForm},
+    instance::Instance,
+  },
+  traits::{ApubActor, Crud},
 };
 use lemmy_db_views_actor::structs::CommunityFollowerView;
 use lemmy_utils::{
@@ -78,7 +83,8 @@ impl ApubObject for ApubCommunity {
   #[tracing::instrument(skip_all)]
   async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
     blocking(context.pool(), move |conn| {
-      Community::update_deleted(conn, self.id, true)
+      let form = CommunityUpdateForm::builder().deleted(Some(true)).build();
+      Community::update(conn, self.id, &form)
     })
     .await??;
     Ok(())
@@ -138,11 +144,17 @@ impl ApubObject for ApubCommunity {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<ApubCommunity, LemmyError> {
-    let form = Group::into_form(group.clone());
+    let apub_id = group.id.inner().to_owned();
+    let instance = blocking(context.pool(), move |conn| {
+      Instance::create_from_actor_id(conn, &apub_id)
+    })
+    .await??;
+
+    let form = Group::into_insert_form(group.clone(), instance.id);
     let languages = LanguageTag::to_language_id_multiple(group.language, context.pool()).await?;
 
     let community: ApubCommunity = blocking(context.pool(), move |conn| {
-      let community = Community::upsert(conn, &form)?;
+      let community = Community::create(conn, &form)?;
       CommunityLanguage::update(conn, languages, community.id)?;
       Ok::<Community, diesel::result::Error>(community)
     })
@@ -205,6 +217,7 @@ impl ApubCommunity {
   ) -> Result<Vec<Url>, LemmyError> {
     let id = self.id;
 
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
     let follows = blocking(context.pool(), move |conn| {
       CommunityFollowerView::for_community(conn, id)
     })
@@ -221,7 +234,10 @@ impl ApubCommunity {
       .unique()
       .filter(|inbox: &Url| inbox.host_str() != Some(&context.settings().hostname))
       // Don't send to blocked instances
-      .filter(|inbox| check_apub_id_valid_with_strictness(inbox, false, context.settings()).is_ok())
+      .filter(|inbox| {
+        check_apub_id_valid_with_strictness(inbox, false, &local_site_data, context.settings())
+          .is_ok()
+      })
       .collect();
 
     Ok(inboxes)
index ef4328ef0582f4856e4333b2159ca39d7912747f..521de64f35167dfddef32a5e84b416b5949f0e0f 100644 (file)
@@ -1,5 +1,6 @@
 use crate::{
   check_apub_id_valid_with_strictness,
+  fetch_local_site_data,
   local_instance,
   objects::read_from_string_or_source_opt,
   protocol::{
@@ -19,12 +20,14 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::utils::blocking;
+use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex};
 use lemmy_db_schema::{
   source::{
     actor_language::SiteLanguage,
-    site::{Site, SiteForm},
+    instance::Instance as DbInstance,
+    site::{Site, SiteInsertForm},
   },
+  traits::Crud,
   utils::{naive_now, DbPool},
 };
 use lemmy_utils::{
@@ -114,10 +117,13 @@ impl ApubObject for ApubSite {
     data: &Self::DataType,
     _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    check_apub_id_valid_with_strictness(apub.id.inner(), true, data.settings())?;
+    let local_site_data = blocking(data.pool(), fetch_local_site_data).await??;
+
+    check_apub_id_valid_with_strictness(apub.id.inner(), true, &local_site_data, data.settings())?;
     verify_domains_match(expected_domain, apub.id.inner())?;
 
-    let slur_regex = &data.settings().slur_regex();
+    let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
+
     check_slurs(&apub.name, slur_regex)?;
     check_slurs_opt(&apub.summary, slur_regex)?;
     Ok(())
@@ -129,27 +135,30 @@ impl ApubObject for ApubSite {
     data: &Self::DataType,
     _request_counter: &mut i32,
   ) -> Result<Self, LemmyError> {
-    let site_form = SiteForm {
+    let apub_id = apub.id.inner().to_owned();
+    let instance = blocking(data.pool(), move |conn| {
+      DbInstance::create_from_actor_id(conn, &apub_id)
+    })
+    .await??;
+
+    let site_form = SiteInsertForm {
       name: apub.name.clone(),
-      sidebar: Some(read_from_string_or_source_opt(
-        &apub.content,
-        &None,
-        &apub.source,
-      )),
+      sidebar: read_from_string_or_source_opt(&apub.content, &None, &apub.source),
       updated: apub.updated.map(|u| u.clone().naive_local()),
-      icon: Some(apub.icon.clone().map(|i| i.url.into())),
-      banner: Some(apub.image.clone().map(|i| i.url.into())),
-      description: Some(apub.summary.clone()),
+      icon: apub.icon.clone().map(|i| i.url.into()),
+      banner: apub.image.clone().map(|i| i.url.into()),
+      description: apub.summary.clone(),
       actor_id: Some(apub.id.clone().into()),
       last_refreshed_at: Some(naive_now()),
       inbox_url: Some(apub.inbox.clone().into()),
       public_key: Some(apub.public_key.public_key_pem.clone()),
-      ..SiteForm::default()
+      private_key: None,
+      instance_id: instance.id,
     };
     let languages = LanguageTag::to_language_id_multiple(apub.language, data.pool()).await?;
 
     let site = blocking(data.pool(), move |conn| {
-      let site = Site::upsert(conn, &site_form)?;
+      let site = Site::create(conn, &site_form)?;
       SiteLanguage::update(conn, languages, site.id)?;
       Ok::<Site, diesel::result::Error>(site)
     })
index 8f11f03b18e2bd62f5e8730463a962c69a460ea5..f54d0756620ab9ce871c56e9cfa9dfc6d4c7e0de 100644 (file)
@@ -67,7 +67,7 @@ pub(crate) mod tests {
   };
   use lemmy_utils::{
     error::LemmyError,
-    rate_limit::{rate_limiter::RateLimiter, RateLimit},
+    rate_limit::{rate_limiter::RateLimiter, RateLimit, RateLimitConfig},
     settings::SETTINGS,
   };
   use lemmy_websocket::{chat_server::ChatServer, LemmyContext};
@@ -96,10 +96,6 @@ pub(crate) mod tests {
     // call this to run migrations
     establish_unpooled_connection();
     let settings = SETTINGS.to_owned();
-    let rate_limiter = RateLimit {
-      rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
-      rate_limit_config: settings.rate_limit.to_owned().unwrap_or_default(),
-    };
     let client = Client::builder()
       .user_agent(build_user_agent(&settings))
       .build()
@@ -122,6 +118,14 @@ pub(crate) mod tests {
     async fn x() -> Result<String, LemmyError> {
       Ok("".to_string())
     }
+
+    let rate_limit_config = RateLimitConfig::builder().build();
+
+    let rate_limiter = RateLimit {
+      rate_limiter: Arc::new(Mutex::new(RateLimiter::default())),
+      rate_limit_config,
+    };
+
     let chat_server = ChatServer::startup(
       pool.clone(),
       rate_limiter,
index b1a892a321244ee9788ea732b98f5977e00eef62..e496da075229511fb128467400246c06db1134a5 100644 (file)
@@ -1,5 +1,6 @@
 use crate::{
   check_apub_id_valid_with_strictness,
+  fetch_local_site_data,
   generate_outbox_url,
   objects::{instance::fetch_instance_actor_for_object, read_from_string_or_source_opt},
   protocol::{
@@ -18,10 +19,13 @@ use activitypub_federation::{
   utils::verify_domains_match,
 };
 use chrono::NaiveDateTime;
-use lemmy_api_common::utils::blocking;
+use lemmy_api_common::utils::{blocking, local_site_opt_to_slur_regex};
 use lemmy_db_schema::{
-  source::person::{Person as DbPerson, PersonForm},
-  traits::ApubActor,
+  source::{
+    instance::Instance,
+    person::{Person as DbPerson, PersonInsertForm, PersonUpdateForm},
+  },
+  traits::{ApubActor, Crud},
   utils::naive_now,
 };
 use lemmy_utils::{
@@ -76,7 +80,8 @@ impl ApubObject for ApubPerson {
   #[tracing::instrument(skip_all)]
   async fn delete(self, context: &LemmyContext) -> Result<(), LemmyError> {
     blocking(context.pool(), move |conn| {
-      DbPerson::update_deleted(conn, self.id, true)
+      let form = PersonUpdateForm::builder().deleted(Some(true)).build();
+      DbPerson::update(conn, self.id, &form)
     })
     .await??;
     Ok(())
@@ -119,12 +124,20 @@ impl ApubObject for ApubPerson {
     context: &LemmyContext,
     _request_counter: &mut i32,
   ) -> Result<(), LemmyError> {
-    verify_domains_match(person.id.inner(), expected_domain)?;
-    check_apub_id_valid_with_strictness(person.id.inner(), false, context.settings())?;
+    let local_site_data = blocking(context.pool(), fetch_local_site_data).await??;
+    let slur_regex = &local_site_opt_to_slur_regex(&local_site_data.local_site);
 
-    let slur_regex = &context.settings().slur_regex();
     check_slurs(&person.preferred_username, slur_regex)?;
     check_slurs_opt(&person.name, slur_regex)?;
+
+    verify_domains_match(person.id.inner(), expected_domain)?;
+    check_apub_id_valid_with_strictness(
+      person.id.inner(),
+      false,
+      &local_site_data,
+      context.settings(),
+    )?;
+
     let bio = read_from_string_or_source_opt(&person.summary, &None, &person.source);
     check_slurs_opt(&bio, slur_regex)?;
     Ok(())
@@ -136,34 +149,37 @@ impl ApubObject for ApubPerson {
     context: &LemmyContext,
     request_counter: &mut i32,
   ) -> Result<ApubPerson, LemmyError> {
-    let person_form = PersonForm {
+    let apub_id = person.id.inner().to_owned();
+    let instance = blocking(context.pool(), move |conn| {
+      Instance::create_from_actor_id(conn, &apub_id)
+    })
+    .await??;
+
+    let person_form = PersonInsertForm {
       name: person.preferred_username,
-      display_name: Some(person.name),
+      display_name: person.name,
       banned: None,
       ban_expires: None,
       deleted: None,
-      avatar: Some(person.icon.map(|i| i.url.into())),
-      banner: Some(person.image.map(|i| i.url.into())),
+      avatar: person.icon.map(|i| i.url.into()),
+      banner: person.image.map(|i| i.url.into()),
       published: person.published.map(|u| u.naive_local()),
       updated: person.updated.map(|u| u.naive_local()),
       actor_id: Some(person.id.into()),
-      bio: Some(read_from_string_or_source_opt(
-        &person.summary,
-        &None,
-        &person.source,
-      )),
+      bio: read_from_string_or_source_opt(&person.summary, &None, &person.source),
       local: Some(false),
       admin: Some(false),
       bot_account: Some(person.kind == UserTypes::Service),
       private_key: None,
-      public_key: Some(person.public_key.public_key_pem),
+      public_key: person.public_key.public_key_pem,
       last_refreshed_at: Some(naive_now()),
       inbox_url: Some(person.inbox.into()),
-      shared_inbox_url: Some(person.endpoints.map(|e| e.shared_inbox.into())),
-      matrix_user_id: Some(person.matrix_user_id),
+      shared_inbox_url: person.endpoints.map(|e| e.shared_inbox.into()),
+      matrix_user_id: person.matrix_user_id,
+      instance_id: instance.id,
     };
     let person = blocking(context.pool(), move |conn| {
-      DbPerson::upsert(conn, &person_form)
+      DbPerson::create(conn, &person_form)
     })
     .await??;
 
@@ -230,22 +246,19 @@ pub(crate) mod tests {
   #[serial]
   async fn test_parse_lemmy_person() {
     let context = init_context();
-    let conn = &mut context.pool().get().unwrap();
     let (person, site) = parse_lemmy_person(&context).await;
 
     assert_eq!(person.display_name, Some("Jean-Luc Picard".to_string()));
     assert!(!person.local);
     assert_eq!(person.bio.as_ref().unwrap().len(), 39);
 
-    DbPerson::delete(conn, person.id).unwrap();
-    Site::delete(conn, site.id).unwrap();
+    cleanup((person, site), &context);
   }
 
   #[actix_rt::test]
   #[serial]
   async fn test_parse_pleroma_person() {
     let context = init_context();
-    let conn = &mut context.pool().get().unwrap();
 
     // create and parse a fake pleroma instance actor, to avoid network request during test
     let mut json: Instance = file_to_json_object("assets/lemmy/objects/instance.json").unwrap();
@@ -272,7 +285,12 @@ pub(crate) mod tests {
     assert_eq!(request_counter, 0);
     assert_eq!(person.bio.as_ref().unwrap().len(), 873);
 
-    DbPerson::delete(conn, person.id).unwrap();
-    Site::delete(conn, site.id).unwrap();
+    cleanup((person, site), &context);
+  }
+
+  fn cleanup(data: (ApubPerson, ApubSite), context: &LemmyContext) {
+    let conn = &mut context.pool().get().unwrap();
+    DbPerson::delete(conn, data.0.id).unwrap();
+    Site::delete(conn, data.1.id).unwrap();
   }
 }
index 655f0342e7849d0f8e084e7fe4e02f34ff92c1ca..2f6f628c2aaf6d07404a60637585a4b7bce1d521 100644 (file)
@@ -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) {
index 3aef60c64d67dfed7ea7347dbd7761bfa5b49e38..bdab7dd003f99e5238c24f343eae1d81aac9d8af 100644 (file)
@@ -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(&note.content, &None, &note.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())
index f6c8f517f98c67d2678c05761f684b6325d902c3..5abfa2b28f582e22efeb1466765f13967430c633 100644 (file)
@@ -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()),
index ea8d3440376e840d82efba364c38f63cb0a0c25d..155d844060ee01fbc523a6190d26bcd6b0a7b860 100644 (file)
@@ -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"
+
index c24e0b94a508afe9a3f91db916d0f996d1d6476e..1130d9101c89341230d09e50d72cd6287dea0d55 100644 (file)
@@ -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();
   }
 }
index 9dfa710fbea0531bf2da8999d27896dbbb830374..1a2c4d2477f9ab75f4b960e665e4baeb16c4112b 100644 (file)
@@ -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();
index ff0cb843711cf6ff3fb71061bf2e1136d2e98512..e9417efb23e04721ada781f5188fb883bb4377ef 100644 (file)
@@ -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();
   }
 }
index 1782d4d69de708d1c4a551cbbb16c331c35f7965..ac7c0eac60066f236b81c475aa412f6129fe7ed1 100644 (file)
@@ -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();
   }
 }
index ed269a8b1f86f0af22c767dbe6ed49ddf7856615..41e82c9588dbe3d614165b183207cc3b65292dd5 100644 (file)
@@ -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();
   }
 }
index 87b78a1d61fb765333a26e0558aa189a5427bbbd..c24a66bd875a4e387fa82b86ad71d972f3a12f51 100644 (file)
@@ -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<Self, Error> {
     use crate::schema::activity::dsl::*;
     activity.find(activity_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, new_activity: &ActivityForm) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, new_activity: &Self::InsertForm) -> Result<Self, Error> {
     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<Self, Error> {
     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<bool>,
   ) -> Result<bool, Error> {
-    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,
     };
 
index c5fa7f32da2f317e3452dee4e59718869cd4fd9d..1de5c6aa608a679b0743c6760fa629f58bf507be 100644 (file)
@@ -61,16 +61,11 @@ impl LocalUserLanguage {
 
 impl SiteLanguage {
   pub fn read_local(conn: &mut PgConnection) -> Result<Vec<LanguageId>, 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();
   }
 }
index eca5328de1eb65ff160281af1d7d5dd7f541f742..d056f1a28572b82311163c82f0005f2fc46ec131 100644 (file)
@@ -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<Self, Error> {
-    use crate::schema::comment::dsl::*;
-
-    diesel::update(comment.find(comment_id))
-      .set(ap_id.eq(apub_id))
-      .get_result::<Self>(conn)
-  }
-
   pub fn permadelete_for_creator(
     conn: &mut PgConnection,
     for_creator_id: PersonId,
@@ -42,28 +31,6 @@ impl Comment {
       .get_results::<Self>(conn)
   }
 
-  pub fn update_deleted(
-    conn: &mut PgConnection,
-    comment_id: CommentId,
-    new_deleted: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::comment::dsl::*;
-    diesel::update(comment.find(comment_id))
-      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_removed(
-    conn: &mut PgConnection,
-    comment_id: CommentId,
-    new_removed: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::comment::dsl::*;
-    diesel::update(comment.find(comment_id))
-      .set((removed.eq(new_removed), updated.eq(naive_now())))
-      .get_result::<Self>(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<Comment, Error> {
     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<Self, Error> {
     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<Self, Error> {
+  fn create(_conn: &mut PgConnection, _comment_form: &Self::InsertForm) -> Result<Self, Error> {
     unimplemented!();
   }
 
   fn update(
     conn: &mut PgConnection,
     comment_id: CommentId,
-    comment_form: &CommentForm,
+    comment_form: &Self::UpdateForm,
   ) -> Result<Self, Error> {
     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);
index 8fed2ce47f03f24ccc2a7c61809938491880e737..84d6fda5146931ee0e283a686380f45d83091109 100644 (file)
@@ -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<Self, Error> {
     use crate::schema::comment_reply::dsl::*;
     comment_reply.find(comment_reply_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, comment_reply_form: &CommentReplyForm) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, comment_reply_form: &Self::InsertForm) -> Result<Self, Error> {
     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<Self, Error> {
     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<CommentReply, Error> {
-    use crate::schema::comment_reply::dsl::*;
-    diesel::update(comment_reply.find(comment_reply_id))
-      .set(read.eq(new_read))
-      .get_result::<Self>(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);
index 966761b2d8f3c0611bb98630502f7e76ae3b2f2e..47c7b0e0d1949b4eb724d504a9f99e671862888e 100644 (file)
@@ -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<Self, Error> {
     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<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     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::<Self>(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<Self, Error> {
     use crate::schema::community::dsl::*;
     diesel::update(community.find(community_id))
-      .set(new_community)
-      .get_result::<Self>(conn)
-  }
-}
-
-impl Community {
-  pub fn update_deleted(
-    conn: &mut PgConnection,
-    community_id: CommunityId,
-    new_deleted: bool,
-  ) -> Result<Community, Error> {
-    use crate::schema::community::dsl::*;
-    diesel::update(community.find(community_id))
-      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_removed(
-    conn: &mut PgConnection,
-    community_id: CommunityId,
-    new_removed: bool,
-  ) -> Result<Community, Error> {
-    use crate::schema::community::dsl::*;
-    diesel::update(community.find(community_id))
-      .set((removed.eq(new_removed), updated.eq(naive_now())))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn distinct_federated_communities(conn: &mut PgConnection) -> Result<Vec<DbUrl>, Error> {
-    use crate::schema::community::dsl::*;
-    community.select(actor_id).distinct().load::<DbUrl>(conn)
-  }
-
-  pub fn upsert(
-    conn: &mut PgConnection,
-    community_form: &CommunityForm,
-  ) -> Result<Community, Error> {
-    use crate::schema::community::dsl::*;
-    insert_into(community)
-      .values(community_form)
-      .on_conflict(actor_id)
-      .do_update()
-      .set(community_form)
-      .get_result::<Self>(conn)
-  }
-
-  pub fn remove_avatar_and_banner(
-    conn: &mut PgConnection,
-    community_id: CommunityId,
-  ) -> Result<Self, Error> {
-    use crate::schema::community::dsl::*;
-    diesel::update(community.find(community_id))
-      .set((
-        icon.eq::<Option<String>>(None),
-        banner.eq::<Option<String>>(None),
-      ))
+      .set(form)
       .get_result::<Self>(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);
index 33415cf0205e74a8fb80a1139d2258ebacdc09d6..c6140f4bc43179f53b59074909e3cc2bc7f0a673 100644 (file)
@@ -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<Self, Error> {
+impl EmailVerification {
+  pub fn create(conn: &mut PgConnection, form: &EmailVerificationForm) -> Result<Self, Error> {
     use crate::schema::email_verification::dsl::*;
     insert_into(email_verification)
       .values(form)
       .get_result::<Self>(conn)
   }
 
-  fn read(conn: &mut PgConnection, id_: i32) -> Result<Self, Error> {
-    use crate::schema::email_verification::dsl::*;
-    email_verification.find(id_).first::<Self>(conn)
-  }
-
-  fn update(
-    conn: &mut PgConnection,
-    id_: i32,
-    form: &EmailVerificationForm,
-  ) -> Result<Self, Error> {
-    use crate::schema::email_verification::dsl::*;
-    diesel::update(email_verification.find(id_))
-      .set(form)
-      .get_result::<Self>(conn)
-  }
-
-  fn delete(conn: &mut PgConnection, id_: i32) -> Result<usize, Error> {
-    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<Self, Error> {
     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 (file)
index 0000000..406b0e2
--- /dev/null
@@ -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<Vec<String>>) -> 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::<Self>(conn)?;
+        }
+        Ok(())
+      } else {
+        Ok(())
+      }
+    })
+  }
+
+  pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
+    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 (file)
index 0000000..58ea4e9
--- /dev/null
@@ -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<Vec<String>>) -> 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::<Self>(conn)?;
+        }
+        Ok(())
+      } else {
+        Ok(())
+      }
+    })
+  }
+
+  pub fn clear(conn: &mut PgConnection) -> Result<usize, Error> {
+    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 (file)
index 0000000..c2afa6a
--- /dev/null
@@ -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<Self, Error> {
+    // Do upsert on domain name conflict
+    insert_into(instance::table)
+      .values(form)
+      .on_conflict(instance::domain)
+      .do_update()
+      .set(form)
+      .get_result::<Self>(conn)
+  }
+  pub fn create(conn: &mut PgConnection, domain: &str) -> Result<Self, Error> {
+    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<Self, Error> {
+    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<usize, Error> {
+    diesel::delete(instance::table.find(instance_id)).execute(conn)
+  }
+  pub fn delete_all(conn: &mut PgConnection) -> Result<usize, Error> {
+    diesel::delete(instance::table).execute(conn)
+  }
+  pub fn allowlist(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
+    instance::table
+      .inner_join(federation_allowlist::table)
+      .select(instance::domain)
+      .load::<String>(conn)
+  }
+
+  pub fn blocklist(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
+    instance::table
+      .inner_join(federation_blocklist::table)
+      .select(instance::domain)
+      .load::<String>(conn)
+  }
+
+  pub fn linked(conn: &mut PgConnection) -> Result<Vec<String>, Error> {
+    instance::table
+      .left_join(federation_blocklist::table)
+      .filter(federation_blocklist::id.is_null())
+      .select(instance::domain)
+      .load::<String>(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 (file)
index 0000000..4686ac8
--- /dev/null
@@ -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<Self, Error> {
+    insert_into(local_site)
+      .values(form)
+      .get_result::<Self>(conn)
+  }
+  pub fn read(conn: &mut PgConnection) -> Result<Self, Error> {
+    local_site.first::<Self>(conn)
+  }
+  pub fn update(conn: &mut PgConnection, form: &LocalSiteUpdateForm) -> Result<Self, Error> {
+    diesel::update(local_site)
+      .set(form)
+      .get_result::<Self>(conn)
+  }
+  pub fn delete(conn: &mut PgConnection) -> Result<usize, Error> {
+    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 (file)
index 0000000..55ab398
--- /dev/null
@@ -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<Self, Error> {
+    local_site_rate_limit::table.first::<Self>(conn)
+  }
+
+  pub fn create(
+    conn: &mut PgConnection,
+    form: &LocalSiteRateLimitInsertForm,
+  ) -> Result<Self, Error> {
+    insert_into(local_site_rate_limit::table)
+      .values(form)
+      .get_result::<Self>(conn)
+  }
+  pub fn update(
+    conn: &mut PgConnection,
+    form: &LocalSiteRateLimitUpdateForm,
+  ) -> Result<Self, Error> {
+    diesel::update(local_site_rate_limit::table)
+      .set(form)
+      .get_result::<Self>(conn)
+  }
+}
index 31eded1a4ce659669f38e441b2759243d7cb1634..9f2c1c3c5fc221adee78246648712ab6a6e220bc 100644 (file)
@@ -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<Self, Error> {
-    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<Self, Error> {
     local_user.find(local_user_id).first::<Self>(conn)
@@ -117,9 +107,14 @@ impl Crud for LocalUser {
   fn delete(conn: &mut PgConnection, local_user_id: LocalUserId) -> Result<usize, Error> {
     diesel::delete(local_user.find(local_user_id)).execute(conn)
   }
-  fn create(conn: &mut PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
+    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::<Self>(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<Self, Error> {
     diesel::update(local_user.find(local_user_id))
       .set(form)
index ba95a18f513303d4157873c37016ea3aaeab0a1e..b1570fe95e9dc7d6e2f51b5f6579ac1b8a2d6e36 100644 (file)
@@ -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;
index 6ec2f81b369b539ab2a2d5b93505a0d4194b61d6..824001deb331f71875867eb585638776e49fcc9c 100644 (file)
@@ -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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
@@ -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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     use crate::schema::admin_purge_person::dsl::*;
     admin_purge_person.find(from_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, form: &Self::Form) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     use crate::schema::admin_purge_person::dsl::*;
     insert_into(admin_purge_person)
       .values(form)
       .get_result::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result<Self, Error> {
+  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result<Self, Error> {
     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<Self, Error> {
     use crate::schema::admin_purge_community::dsl::*;
     admin_purge_community.find(from_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, form: &Self::Form) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     use crate::schema::admin_purge_community::dsl::*;
     insert_into(admin_purge_community)
       .values(form)
       .get_result::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result<Self, Error> {
+  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result<Self, Error> {
     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<Self, Error> {
     use crate::schema::admin_purge_post::dsl::*;
     admin_purge_post.find(from_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, form: &Self::Form) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     use crate::schema::admin_purge_post::dsl::*;
     insert_into(admin_purge_post)
       .values(form)
       .get_result::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result<Self, Error> {
+  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result<Self, Error> {
     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<Self, Error> {
     use crate::schema::admin_purge_comment::dsl::*;
     admin_purge_comment.find(from_id).first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, form: &Self::Form) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     use crate::schema::admin_purge_comment::dsl::*;
     insert_into(admin_purge_comment)
       .values(form)
       .get_result::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::Form) -> Result<Self, Error> {
+  fn update(conn: &mut PgConnection, from_id: i32, form: &Self::InsertForm) -> Result<Self, Error> {
     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);
index dfa56e209508161d3676676ef8632e19062baf02..7a4d4c8393bf4b9c8379ec217caf932c62cfcb94 100644 (file)
@@ -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<Self, Error> {
     password_reset_request
@@ -74,7 +75,8 @@ fn bytes_to_hex(bytes: Vec<u8>) -> 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!(
index 9c5dc5b65ca4d6597538ebb9bf7db6b6d00e6d84..22a26440d593c69cf4aae9c7008d97c2ee52fc92 100644 (file)
@@ -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<Self, Error> {
     person
@@ -78,13 +81,18 @@ impl Crud for Person {
   fn delete(conn: &mut PgConnection, person_id: PersonId) -> Result<usize, Error> {
     diesel::delete(person.find(person_id)).execute(conn)
   }
-  fn create(conn: &mut PgConnection, form: &PersonForm) -> Result<Self, Error> {
-    insert_into(person).values(form).get_result::<Self>(conn)
+  fn create(conn: &mut PgConnection, form: &PersonInsertForm) -> Result<Self, Error> {
+    insert_into(person)
+      .values(form)
+      .on_conflict(actor_id)
+      .do_update()
+      .set(form)
+      .get_result::<Self>(conn)
   }
   fn update(
     conn: &mut PgConnection,
     person_id: PersonId,
-    form: &PersonForm,
+    form: &PersonUpdateForm,
   ) -> Result<Self, Error> {
     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<chrono::NaiveDateTime>,
-  ) -> Result<Self, Error> {
-    diesel::update(person.find(person_id))
-      .set((banned.eq(ban), ban_expires.eq(expires)))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn add_admin(
-    conn: &mut PgConnection,
-    person_id: PersonId,
-    added: bool,
-  ) -> Result<Self, Error> {
-    diesel::update(person.find(person_id))
-      .set(admin.eq(added))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn mark_as_updated(conn: &mut PgConnection, person_id: PersonId) -> Result<Person, Error> {
-    diesel::update(person.find(person_id))
-      .set((last_refreshed_at.eq(naive_now()),))
-      .get_result::<Self>(conn)
-  }
-
   pub fn delete_account(conn: &mut PgConnection, person_id: PersonId) -> Result<Person, Error> {
     use crate::schema::local_user;
 
@@ -143,44 +124,6 @@ impl Person {
       ))
       .get_result::<Self>(conn)
   }
-
-  pub fn upsert(conn: &mut PgConnection, person_form: &PersonForm) -> Result<Person, Error> {
-    insert_into(person)
-      .values(person_form)
-      .on_conflict(actor_id)
-      .do_update()
-      .set(person_form)
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_deleted(
-    conn: &mut PgConnection,
-    person_id: PersonId,
-    new_deleted: bool,
-  ) -> Result<Person, Error> {
-    use crate::schema::person::dsl::*;
-    diesel::update(person.find(person_id))
-      .set(deleted.eq(new_deleted))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn leave_admin(conn: &mut PgConnection, person_id: PersonId) -> Result<Self, Error> {
-    diesel::update(person.find(person_id))
-      .set(admin.eq(false))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn remove_avatar_and_banner(
-    conn: &mut PgConnection,
-    person_id: PersonId,
-  ) -> Result<Self, Error> {
-    diesel::update(person.find(person_id))
-      .set((
-        avatar.eq::<Option<String>>(None),
-        banner.eq::<Option<String>>(None),
-      ))
-      .get_result::<Self>(conn)
-  }
 }
 
 pub fn is_banned(banned_: bool, expires: Option<chrono::NaiveDateTime>) -> 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);
index fc91c76e5b3424a550f5a8f2e8ae8994ef86cbe0..e83de1feb4dd7ae54e68f6905b4e707d39bd6d9b 100644 (file)
@@ -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<Self, Error> {
     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<Self, Error> {
     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<Self, Error> {
     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<PersonMention, Error> {
-    use crate::schema::person_mention::dsl::*;
-    diesel::update(person_mention.find(person_mention_id))
-      .set(read.eq(new_read))
-      .get_result::<Self>(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::<Self>(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);
index 4fa60159294d4fcad67d5c5f9cc3edcf4610bfd5..95535b194dbaea60a3c14dcc7659d87979cb343a 100644 (file)
@@ -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<Self, Error> {
     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<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
-    insert_into(post).values(new_post).get_result::<Self>(conn)
+    insert_into(post)
+      .values(form)
+      .on_conflict(ap_id)
+      .do_update()
+      .set(form)
+      .get_result::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, post_id: PostId, new_post: &PostForm) -> Result<Self, Error> {
+  fn update(
+    conn: &mut PgConnection,
+    post_id: PostId,
+    new_post: &Self::UpdateForm,
+  ) -> Result<Self, Error> {
     use crate::schema::post::dsl::*;
     diesel::update(post.find(post_id))
       .set(new_post)
@@ -58,18 +69,6 @@ impl Post {
       .load::<Self>(conn)
   }
 
-  pub fn update_ap_id(
-    conn: &mut PgConnection,
-    post_id: PostId,
-    apub_id: DbUrl,
-  ) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-
-    diesel::update(post.find(post_id))
-      .set(ap_id.eq(apub_id))
-      .get_result::<Self>(conn)
-  }
-
   pub fn permadelete_for_creator(
     conn: &mut PgConnection,
     for_creator_id: PersonId,
@@ -90,28 +89,6 @@ impl Post {
       .get_results::<Self>(conn)
   }
 
-  pub fn update_deleted(
-    conn: &mut PgConnection,
-    post_id: PostId,
-    new_deleted: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::update(post.find(post_id))
-      .set((deleted.eq(new_deleted), updated.eq(naive_now())))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_removed(
-    conn: &mut PgConnection,
-    post_id: PostId,
-    new_removed: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::update(post.find(post_id))
-      .set((removed.eq(new_removed), updated.eq(naive_now())))
-      .get_result::<Self>(conn)
-  }
-
   pub fn update_removed_for_creator(
     conn: &mut PgConnection,
     for_creator_id: PersonId,
@@ -132,41 +109,10 @@ impl Post {
       .get_results::<Self>(conn)
   }
 
-  pub fn update_locked(
-    conn: &mut PgConnection,
-    post_id: PostId,
-    new_locked: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::update(post.find(post_id))
-      .set(locked.eq(new_locked))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_stickied(
-    conn: &mut PgConnection,
-    post_id: PostId,
-    new_stickied: bool,
-  ) -> Result<Self, Error> {
-    use crate::schema::post::dsl::*;
-    diesel::update(post.find(post_id))
-      .set(stickied.eq(new_stickied))
-      .get_result::<Self>(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<Post, Error> {
-    use crate::schema::post::dsl::*;
-    insert_into(post)
-      .values(post_form)
-      .on_conflict(ap_id)
-      .do_update()
-      .set(post_form)
-      .get_result::<Self>(conn)
-  }
   pub fn read_from_apub_id(conn: &mut PgConnection, object_id: Url) -> Result<Option<Self>, 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);
index 2ec1702eae418681aa3687a9e5df1cb029633bf1..80818ee3450ebfb14797c965c7de244f0dc6c2a2 100644 (file)
@@ -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<Self, Error> {
     use crate::schema::private_message::dsl::*;
     private_message.find(private_message_id).first::<Self>(conn)
   }
 
-  fn create(
-    conn: &mut PgConnection,
-    private_message_form: &PrivateMessageForm,
-  ) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     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::<Self>(conn)
   }
 
   fn update(
     conn: &mut PgConnection,
     private_message_id: PrivateMessageId,
-    private_message_form: &PrivateMessageForm,
+    form: &Self::UpdateForm,
   ) -> Result<Self, Error> {
     use crate::schema::private_message::dsl::*;
     diesel::update(private_message.find(private_message_id))
-      .set(private_message_form)
+      .set(form)
       .get_result::<Self>(conn)
   }
   fn delete(conn: &mut PgConnection, pm_id: Self::IdType) -> Result<usize, Error> {
@@ -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<PrivateMessage, Error> {
-    use crate::schema::private_message::dsl::*;
-
-    diesel::update(private_message.find(private_message_id))
-      .set(ap_id.eq(apub_id))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_content(
-    conn: &mut PgConnection,
-    private_message_id: PrivateMessageId,
-    new_content: &str,
-  ) -> Result<PrivateMessage, Error> {
-    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::<Self>(conn)
-  }
-
-  pub fn update_deleted(
-    conn: &mut PgConnection,
-    private_message_id: PrivateMessageId,
-    new_deleted: bool,
-  ) -> Result<PrivateMessage, Error> {
-    use crate::schema::private_message::dsl::*;
-    diesel::update(private_message.find(private_message_id))
-      .set(deleted.eq(new_deleted))
-      .get_result::<Self>(conn)
-  }
-
-  pub fn update_read(
-    conn: &mut PgConnection,
-    private_message_id: PrivateMessageId,
-    new_read: bool,
-  ) -> Result<PrivateMessage, Error> {
-    use crate::schema::private_message::dsl::*;
-    diesel::update(private_message.find(private_message_id))
-      .set(read.eq(new_read))
-      .get_result::<Self>(conn)
-  }
-
   pub fn mark_all_as_read(
     conn: &mut PgConnection,
     for_recipient_id: PersonId,
@@ -102,19 +57,6 @@ impl PrivateMessage {
     .get_results::<Self>(conn)
   }
 
-  pub fn upsert(
-    conn: &mut PgConnection,
-    private_message_form: &PrivateMessageForm,
-  ) -> Result<PrivateMessage, Error> {
-    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::<Self>(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);
index 6868a1da763091200f68a2f270e1599703bafb7c..8004abc6c3f5e883c60d4efe020d74ef70c68dcb 100644 (file)
@@ -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<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     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::<Self>(conn)
   }
 
-  fn update(conn: &mut PgConnection, id_: Self::IdType, form: &Self::Form) -> Result<Self, Error> {
+  fn update(
+    conn: &mut PgConnection,
+    id_: Self::IdType,
+    form: &Self::UpdateForm,
+  ) -> Result<Self, Error> {
     use crate::schema::registration_application::dsl::*;
     diesel::update(registration_application.find(id_))
       .set(form)
index ef80b6f647a5fd2e0543d2d70d9ffe2c568bc7e8..bea9a21c51eb269e1f09bcc92b6fd1fb3c04a43c 100644 (file)
@@ -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<Self, Error> {
     use crate::schema::site::dsl::*;
     site.first::<Self>(conn)
   }
 
-  fn create(conn: &mut PgConnection, new_site: &SiteForm) -> Result<Self, Error> {
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error> {
     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::<Self>(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<Self, Error> {
+  fn update(
+    conn: &mut PgConnection,
+    site_id: SiteId,
+    new_site: &Self::UpdateForm,
+  ) -> Result<Self, Error> {
     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<Self, Error> {
-    use crate::schema::site::dsl::*;
-    site.order_by(id).first::<Self>(conn)
-  }
-
-  pub fn upsert(conn: &mut PgConnection, site_form: &SiteForm) -> Result<Site, Error> {
-    use crate::schema::site::dsl::*;
-    insert_into(site)
-      .values(site_form)
-      .on_conflict(actor_id)
-      .do_update()
-      .set(site_form)
-      .get_result::<Self>(conn)
-  }
-
   pub fn read_from_apub_id(conn: &mut PgConnection, object_id: Url) -> Result<Option<Self>, 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<Vec<Self>, Error> {
     use crate::schema::site::dsl::*;
     site.order_by(id).offset(1).get_results::<Self>(conn)
index 086c7029cee953b74f9ef1a57d5f2d4094c9fd31..a859b2aabbed01055c835a18407d1333155180ad 100644 (file)
@@ -1,3 +1,5 @@
+#![recursion_limit = "256"]
+
 #[cfg(feature = "full")]
 #[macro_use]
 extern crate diesel;
index d0287e1558643cfebf5eebae860d3d94d4a1ae9c..147688370ec48b6b8a6757e98a4daaa8b3fea3b0 100644 (file)
@@ -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))]
index cc72bb9bc886f8669586601419583b9411a60c39..9f0b1eb9da21a9893058c4b9966ae18849da9ceb 100644 (file)
@@ -100,6 +100,7 @@ table! {
         shared_inbox_url -> Nullable<Varchar>,
         hidden -> Bool,
         posting_restricted_to_mods -> Bool,
+        instance_id -> Int4,
     }
 }
 
@@ -313,6 +314,7 @@ table! {
         admin -> Bool,
         bot_account -> Bool,
         ban_expires -> Nullable<Timestamp>,
+        instance_id -> Int4,
     }
 }
 
@@ -485,27 +487,15 @@ table! {
         sidebar -> Nullable<Text>,
         published -> Timestamp,
         updated -> Nullable<Timestamp>,
-        enable_downvotes -> Bool,
-        open_registration -> Bool,
-        enable_nsfw -> Bool,
         icon -> Nullable<Varchar>,
         banner -> Nullable<Varchar>,
         description -> Nullable<Text>,
-        community_creation_admin_only -> Bool,
-        require_email_verification -> Bool,
-        require_application -> Bool,
-        application_question -> Nullable<Text>,
-        private_instance -> Bool,
         actor_id -> Text,
         last_refreshed_at -> Timestamp,
         inbox_url -> Text,
         private_key -> Nullable<Text>,
         public_key -> Text,
-        default_theme -> Text,
-        default_post_listing_type -> Text,
-        legal_information -> Nullable<Text>,
-        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<Timestamp>,
+  }
+}
+
+table! {
+  federation_allowlist(id) {
+    id -> Int4,
+    instance_id -> Int4,
+    published -> Timestamp,
+    updated -> Nullable<Timestamp>,
+  }
+}
+
+table! {
+  federation_blocklist(id) {
+    id -> Int4,
+    instance_id -> Int4,
+    published -> Timestamp,
+    updated -> Nullable<Timestamp>,
+  }
+}
+
+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<Text>,
+    private_instance -> Bool,
+    default_theme -> Text,
+    default_post_listing_type -> Text,
+    legal_information -> Nullable<Text>,
+    hide_modlog_mod_names -> Bool,
+    application_email_admins -> Bool,
+    slur_filter_regex -> Nullable<Text>,
+    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<Timestamp>,
+  }
+}
+
+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<Timestamp>,
+  }
+}
+
 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,
 );
index 2d256a5b3c894dfabae7cbbf99ebb920d4116717..55d7d6fb8458033906949727b356c9ef2e34e112 100644 (file)
@@ -14,12 +14,21 @@ pub struct Activity {
   pub sensitive: Option<bool>,
 }
 
-#[derive(Insertable, AsChangeset)]
+#[derive(Insertable)]
 #[diesel(table_name = activity)]
-pub struct ActivityForm {
+pub struct ActivityInsertForm {
   pub data: Value,
   pub local: Option<bool>,
   pub updated: Option<chrono::NaiveDateTime>,
   pub ap_id: DbUrl,
-  pub sensitive: bool,
+  pub sensitive: Option<bool>,
+}
+
+#[derive(AsChangeset)]
+#[diesel(table_name = activity)]
+pub struct ActivityUpdateForm {
+  pub data: Option<Value>,
+  pub local: Option<bool>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+  pub sensitive: Option<Option<bool>>,
 }
index d9117bae134270b4d2ed3f78a5b9bab6b66d207c..5b7ab085b6275e5d2241a14be4626b57fb925b25 100644 (file)
@@ -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<bool>,
   pub published: Option<chrono::NaiveDateTime>,
@@ -43,6 +48,22 @@ pub struct CommentForm {
   pub language_id: Option<LanguageId>,
 }
 
+#[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<String>,
+  pub removed: Option<bool>,
+  // Don't use a default naive_now here, because the create function does a lot of comment updates
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+  pub deleted: Option<bool>,
+  pub ap_id: Option<DbUrl>,
+  pub local: Option<bool>,
+  pub distinguished: Option<bool>,
+  pub language_id: Option<LanguageId>,
+}
+
 #[derive(PartialEq, Eq, Debug, Clone)]
 #[cfg_attr(feature = "full", derive(Identifiable, Queryable, Associations))]
 #[cfg_attr(feature = "full", diesel(belongs_to(crate::source::comment::Comment)))]
index 737d8df6a5aca9f7b34252eb9e47010f69fe1760..8b7fe51b027252fece2a43773f8dcc0da76df44d 100644 (file)
@@ -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<bool>,
 }
+
+#[cfg_attr(feature = "full", derive(AsChangeset))]
+#[cfg_attr(feature = "full", diesel(table_name = comment_reply))]
+pub struct CommentReplyUpdateForm {
+  pub read: Option<bool>,
+}
index 074141cc822727def2eb895fdc09ceea470ecc71..6d988ae75f427f4a695ece0a315851d75d1fca8b 100644 (file)
@@ -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<DbUrl>,
   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<DbUrl>,
   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<Option<String>>,
+  pub description: Option<String>,
   pub removed: Option<bool>,
   pub published: Option<chrono::NaiveDateTime>,
   pub updated: Option<chrono::NaiveDateTime>,
@@ -67,8 +73,36 @@ pub struct CommunityForm {
   pub nsfw: Option<bool>,
   pub actor_id: Option<DbUrl>,
   pub local: Option<bool>,
-  pub private_key: Option<Option<String>>,
+  pub private_key: Option<String>,
+  pub public_key: String,
+  pub last_refreshed_at: Option<chrono::NaiveDateTime>,
+  pub icon: Option<DbUrl>,
+  pub banner: Option<DbUrl>,
+  pub followers_url: Option<DbUrl>,
+  pub inbox_url: Option<DbUrl>,
+  pub shared_inbox_url: Option<DbUrl>,
+  pub hidden: Option<bool>,
+  pub posting_restricted_to_mods: Option<bool>,
+  #[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<String>,
+  pub description: Option<Option<String>>,
+  pub removed: Option<bool>,
+  pub published: Option<chrono::NaiveDateTime>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+  pub deleted: Option<bool>,
+  pub nsfw: Option<bool>,
+  pub actor_id: Option<DbUrl>,
+  pub local: Option<bool>,
   pub public_key: Option<String>,
+  pub private_key: Option<Option<String>>,
   pub last_refreshed_at: Option<chrono::NaiveDateTime>,
   pub icon: Option<Option<DbUrl>>,
   pub banner: Option<Option<DbUrl>>,
diff --git a/crates/db_schema/src/source/federation_allowlist.rs b/crates/db_schema/src/source/federation_allowlist.rs
new file mode 100644 (file)
index 0000000..5ca5927
--- /dev/null
@@ -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<chrono::NaiveDateTime>,
+}
+
+#[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<chrono::NaiveDateTime>,
+}
diff --git a/crates/db_schema/src/source/federation_blocklist.rs b/crates/db_schema/src/source/federation_blocklist.rs
new file mode 100644 (file)
index 0000000..70f7a08
--- /dev/null
@@ -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<chrono::NaiveDateTime>,
+}
+
+#[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<chrono::NaiveDateTime>,
+}
diff --git a/crates/db_schema/src/source/instance.rs b/crates/db_schema/src/source/instance.rs
new file mode 100644 (file)
index 0000000..0b86f9c
--- /dev/null
@@ -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<chrono::NaiveDateTime>,
+}
+
+#[cfg_attr(feature = "full", derive(Insertable, AsChangeset))]
+#[cfg_attr(feature = "full", diesel(table_name = instance))]
+pub struct InstanceForm {
+  pub domain: String,
+  pub updated: Option<chrono::NaiveDateTime>,
+}
diff --git a/crates/db_schema/src/source/local_site.rs b/crates/db_schema/src/source/local_site.rs
new file mode 100644 (file)
index 0000000..443923a
--- /dev/null
@@ -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<String>,
+  pub private_instance: bool,
+  pub default_theme: String,
+  pub default_post_listing_type: String,
+  pub legal_information: Option<String>,
+  pub hide_modlog_mod_names: bool,
+  pub application_email_admins: bool,
+  pub slur_filter_regex: Option<String>,
+  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<chrono::NaiveDateTime>,
+}
+
+#[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<bool>,
+  pub enable_downvotes: Option<bool>,
+  pub open_registration: Option<bool>,
+  pub enable_nsfw: Option<bool>,
+  pub community_creation_admin_only: Option<bool>,
+  pub require_email_verification: Option<bool>,
+  pub require_application: Option<bool>,
+  pub application_question: Option<String>,
+  pub private_instance: Option<bool>,
+  pub default_theme: Option<String>,
+  pub default_post_listing_type: Option<String>,
+  pub legal_information: Option<String>,
+  pub hide_modlog_mod_names: Option<bool>,
+  pub application_email_admins: Option<bool>,
+  pub slur_filter_regex: Option<String>,
+  pub actor_name_max_length: Option<i32>,
+  pub federation_enabled: Option<bool>,
+  pub federation_debug: Option<bool>,
+  pub federation_strict_allowlist: Option<bool>,
+  pub federation_http_fetch_retry_limit: Option<i32>,
+  pub federation_worker_count: Option<i32>,
+  pub captcha_enabled: Option<bool>,
+  pub captcha_difficulty: Option<String>,
+}
+
+#[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<bool>,
+  pub enable_downvotes: Option<bool>,
+  pub open_registration: Option<bool>,
+  pub enable_nsfw: Option<bool>,
+  pub community_creation_admin_only: Option<bool>,
+  pub require_email_verification: Option<bool>,
+  pub require_application: Option<bool>,
+  pub application_question: Option<Option<String>>,
+  pub private_instance: Option<bool>,
+  pub default_theme: Option<String>,
+  pub default_post_listing_type: Option<String>,
+  pub legal_information: Option<Option<String>>,
+  pub hide_modlog_mod_names: Option<bool>,
+  pub application_email_admins: Option<bool>,
+  pub slur_filter_regex: Option<Option<String>>,
+  pub actor_name_max_length: Option<i32>,
+  pub federation_enabled: Option<bool>,
+  pub federation_debug: Option<bool>,
+  pub federation_strict_allowlist: Option<bool>,
+  pub federation_http_fetch_retry_limit: Option<i32>,
+  pub federation_worker_count: Option<i32>,
+  pub captcha_enabled: Option<bool>,
+  pub captcha_difficulty: Option<String>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+}
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 (file)
index 0000000..bd89013
--- /dev/null
@@ -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<chrono::NaiveDateTime>,
+}
+
+#[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<i32>,
+  pub message_per_second: Option<i32>,
+  pub post: Option<i32>,
+  pub post_per_second: Option<i32>,
+  pub register: Option<i32>,
+  pub register_per_second: Option<i32>,
+  pub image: Option<i32>,
+  pub image_per_second: Option<i32>,
+  pub comment: Option<i32>,
+  pub comment_per_second: Option<i32>,
+  pub search: Option<i32>,
+  pub search_per_second: Option<i32>,
+}
+
+#[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<i32>,
+  pub message_per_second: Option<i32>,
+  pub post: Option<i32>,
+  pub post_per_second: Option<i32>,
+  pub register: Option<i32>,
+  pub register_per_second: Option<i32>,
+  pub image: Option<i32>,
+  pub image_per_second: Option<i32>,
+  pub comment: Option<i32>,
+  pub comment_per_second: Option<i32>,
+  pub search: Option<i32>,
+  pub search_per_second: Option<i32>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+}
index 4b3a3414484c24ccd400a261d7d2d417e4836e8f..04fb4b87b64e399a0cb15a1950dd38537f90c299 100644 (file)
@@ -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<PersonId>,
-  pub password_encrypted: Option<String>,
-  pub email: Option<Option<String>>,
-  pub show_nsfw: Option<bool>,
-  pub theme: Option<String>,
-  pub default_sort_type: Option<i16>,
-  pub default_listing_type: Option<i16>,
-  pub interface_language: Option<String>,
-  pub show_avatars: Option<bool>,
-  pub send_notifications_to_email: Option<bool>,
-  pub show_bot_accounts: Option<bool>,
-  pub show_scores: Option<bool>,
-  pub show_read_posts: Option<bool>,
-  pub show_new_post_notifs: Option<bool>,
-  pub email_verified: Option<bool>,
-  pub accepted_application: Option<bool>,
-}
-
 /// 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<String>,
+  pub show_nsfw: Option<bool>,
+  pub theme: Option<String>,
+  pub default_sort_type: Option<i16>,
+  pub default_listing_type: Option<i16>,
+  pub interface_language: Option<String>,
+  pub show_avatars: Option<bool>,
+  pub send_notifications_to_email: Option<bool>,
+  pub show_bot_accounts: Option<bool>,
+  pub show_scores: Option<bool>,
+  pub show_read_posts: Option<bool>,
+  pub show_new_post_notifs: Option<bool>,
+  pub email_verified: Option<bool>,
+  pub accepted_application: Option<bool>,
+}
+
+#[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<String>,
+  pub email: Option<Option<String>>,
+  pub show_nsfw: Option<bool>,
+  pub theme: Option<String>,
+  pub default_sort_type: Option<i16>,
+  pub default_listing_type: Option<i16>,
+  pub interface_language: Option<String>,
+  pub show_avatars: Option<bool>,
+  pub send_notifications_to_email: Option<bool>,
+  pub show_bot_accounts: Option<bool>,
+  pub show_scores: Option<bool>,
+  pub show_read_posts: Option<bool>,
+  pub show_new_post_notifs: Option<bool>,
+  pub email_verified: Option<bool>,
+  pub accepted_application: Option<bool>,
+}
index 676acd8f6ce79c0c32cdcdf613b9d2c17b2ebdc1..b8e091fc0ca2463c4aa549aaccba2c8206566106 100644 (file)
@@ -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;
index 5d9d603de85d1d64b31bcaf70511304cb6744f8a..0d2706c51aa0e73a7b5517d04474ac8aceb14480 100644 (file)
@@ -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<chrono::NaiveDateTime>,
+  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<chrono::NaiveDateTime>,
+  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<Option<String>>,
-  pub avatar: Option<Option<DbUrl>>,
+  #[builder(!default)]
+  pub public_key: String,
+  #[builder(!default)]
+  pub instance_id: InstanceId,
+  pub display_name: Option<String>,
+  pub avatar: Option<DbUrl>,
   pub banned: Option<bool>,
   pub published: Option<chrono::NaiveDateTime>,
   pub updated: Option<chrono::NaiveDateTime>,
   pub actor_id: Option<DbUrl>,
+  pub bio: Option<String>,
+  pub local: Option<bool>,
+  pub private_key: Option<String>,
+  pub last_refreshed_at: Option<chrono::NaiveDateTime>,
+  pub banner: Option<DbUrl>,
+  pub deleted: Option<bool>,
+  pub inbox_url: Option<DbUrl>,
+  pub shared_inbox_url: Option<DbUrl>,
+  pub matrix_user_id: Option<String>,
+  pub admin: Option<bool>,
+  pub bot_account: Option<bool>,
+  pub ban_expires: Option<chrono::NaiveDateTime>,
+}
+
+#[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<Option<String>>,
+  pub avatar: Option<Option<DbUrl>>,
+  pub banned: Option<bool>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+  pub actor_id: Option<DbUrl>,
   pub bio: Option<Option<String>>,
   pub local: Option<bool>,
-  pub private_key: Option<Option<String>>,
   pub public_key: Option<String>,
+  pub private_key: Option<Option<String>>,
   pub last_refreshed_at: Option<chrono::NaiveDateTime>,
   pub banner: Option<Option<DbUrl>>,
   pub deleted: Option<bool>,
index a12563e5f20f49e342b9cba4008a4cc0ee5d0dd9..b4a2ce59c090e72846f1c5977e67c6de414e05c4 100644 (file)
@@ -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<bool>,
 }
+
+#[cfg_attr(feature = "full", derive(AsChangeset))]
+#[cfg_attr(feature = "full", diesel(table_name = person_mention))]
+pub struct PersonMentionUpdateForm {
+  pub read: Option<bool>,
+}
index e9f383fe5a6e58685471f0b366bff20e86466e7e..9af6a3f296f5b64a8bf036efb8cc7ac50afa33a0 100644 (file)
@@ -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<bool>,
+  pub url: Option<DbUrl>,
+  pub body: Option<String>,
+  pub removed: Option<bool>,
+  pub locked: Option<bool>,
+  pub updated: Option<chrono::NaiveDateTime>,
+  pub published: Option<chrono::NaiveDateTime>,
+  pub deleted: Option<bool>,
+  pub stickied: Option<bool>,
+  pub embed_title: Option<String>,
+  pub embed_description: Option<String>,
+  pub embed_video_url: Option<DbUrl>,
+  pub thumbnail_url: Option<DbUrl>,
+  pub ap_id: Option<DbUrl>,
+  pub local: Option<bool>,
+  pub language_id: Option<LanguageId>,
+}
+
+#[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<String>,
+  pub nsfw: Option<bool>,
   pub url: Option<Option<DbUrl>>,
   pub body: Option<Option<String>>,
   pub removed: Option<bool>,
   pub locked: Option<bool>,
   pub published: Option<chrono::NaiveDateTime>,
-  pub updated: Option<chrono::NaiveDateTime>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
   pub deleted: Option<bool>,
   pub stickied: Option<bool>,
   pub embed_title: Option<Option<String>>,
index 9b2cb342ace8f8cb8fb2ef7f38bb00a76660248d..f6e969f37df4c18e4a76c3b63d8a992ab05fb390 100644 (file)
@@ -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<bool>,
   pub read: Option<bool>,
@@ -38,3 +43,17 @@ pub struct PrivateMessageForm {
   pub ap_id: Option<DbUrl>,
   pub local: Option<bool>,
 }
+
+#[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<String>,
+  pub deleted: Option<bool>,
+  pub read: Option<bool>,
+  pub published: Option<chrono::NaiveDateTime>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
+  pub ap_id: Option<DbUrl>,
+  pub local: Option<bool>,
+}
index ecf4eb3e875d116381f80d6539ea7983d8dc26cd..40f00bdfabff9b003db896033d4c191a80a5a27c 100644 (file)
@@ -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<LocalUserId>,
-  pub answer: Option<String>,
-  pub admin_id: Option<PersonId>,
+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<Option<PersonId>>,
   pub deny_reason: Option<Option<String>>,
 }
index d550ab38f4e76a94d052363ef2f83fcb71080739..8c4a5b1ca2c75001eff3183ac7192870628f426e 100644 (file)
@@ -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<String>,
   pub published: chrono::NaiveDateTime,
   pub updated: Option<chrono::NaiveDateTime>,
-  pub enable_downvotes: bool,
-  pub open_registration: bool,
-  pub enable_nsfw: bool,
   pub icon: Option<DbUrl>,
   pub banner: Option<DbUrl>,
   pub description: Option<String>,
-  pub community_creation_admin_only: bool,
-  pub require_email_verification: bool,
-  pub require_application: bool,
-  pub application_question: Option<String>,
-  pub private_instance: bool,
   pub actor_id: DbUrl,
   pub last_refreshed_at: chrono::NaiveDateTime,
   pub inbox_url: DbUrl,
   pub private_key: Option<String>,
   pub public_key: String,
-  pub default_theme: String,
-  pub default_post_listing_type: String,
-  pub legal_information: Option<String>,
-  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<Option<String>>,
+  pub sidebar: Option<String>,
   pub updated: Option<chrono::NaiveDateTime>,
-  pub enable_downvotes: Option<bool>,
-  pub open_registration: Option<bool>,
-  pub enable_nsfw: Option<bool>,
+  pub icon: Option<DbUrl>,
+  pub banner: Option<DbUrl>,
+  pub description: Option<String>,
+  pub actor_id: Option<DbUrl>,
+  pub last_refreshed_at: Option<chrono::NaiveDateTime>,
+  pub inbox_url: Option<DbUrl>,
+  pub private_key: Option<String>,
+  pub public_key: Option<String>,
+  #[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<String>,
+  pub sidebar: Option<Option<String>>,
+  pub updated: Option<Option<chrono::NaiveDateTime>>,
   // 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<Option<DbUrl>>,
   pub banner: Option<Option<DbUrl>>,
   pub description: Option<Option<String>>,
-  pub community_creation_admin_only: Option<bool>,
-  pub require_email_verification: Option<bool>,
-  pub require_application: Option<bool>,
-  pub application_question: Option<Option<String>>,
-  pub private_instance: Option<bool>,
   pub actor_id: Option<DbUrl>,
   pub last_refreshed_at: Option<chrono::NaiveDateTime>,
   pub inbox_url: Option<DbUrl>,
   pub private_key: Option<Option<String>>,
   pub public_key: Option<String>,
-  pub default_theme: Option<String>,
-  pub default_post_listing_type: Option<String>,
-  pub legal_information: Option<Option<String>>,
-  pub application_email_admins: Option<bool>,
-  pub hide_modlog_mod_names: Option<bool>,
 }
index 102d0c0b884516740351dd3aa7f289a7309c1e4c..6e6d2c700ac493fcf1ab00398ebbf0f4af5b0b37 100644 (file)
@@ -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<Self, Error>
+  fn create(conn: &mut PgConnection, form: &Self::InsertForm) -> Result<Self, Error>
   where
     Self: Sized;
   fn read(conn: &mut PgConnection, id: Self::IdType) -> Result<Self, Error>
   where
     Self: Sized;
-  fn update(conn: &mut PgConnection, id: Self::IdType, form: &Self::Form) -> Result<Self, Error>
+  /// 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<Self, Error>
   where
     Self: Sized;
   fn delete(_conn: &mut PgConnection, _id: Self::IdType) -> Result<usize, Error>
index 798786b06d1cd829b6199f294debba952dcbdd63..d20b32d8deff6903c13c70c9585328c1ab2d9f89 100644 (file)
@@ -100,6 +100,20 @@ pub fn diesel_option_overwrite_to_url(
   }
 }
 
+pub fn diesel_option_overwrite_to_url_create(
+  opt: &Option<String>,
+) -> Result<Option<DbUrl>, 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 {
index 3046c4e78229c9f18a86300a244a07e83c7f9c61..1a9e31eb96800e97ecabfa8d2503780e69992b0f 100644 (file)
@@ -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"
index 50564b0ab72a7fd471842e01f905ba44b227f14c..e4a6a493c669242bc18796ec255b8c5698cc7a32 100644 (file)
@@ -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();
   }
 }
index 5a64a298b487f3b0ea163aa1edb05c6705b57fb2..5a94d37a52d0d959711e2ff209f7b374ff59673b 100644 (file)
@@ -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,
index 472795b5049a5ab44dd8b4956b603132ae57b8cd..83c43ff4bd104f8cbc712406e226ad3d37605eae 100644 (file)
@@ -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();
   }
 }
index 9db1d095c9ef935d5bdb0740bed7be193c0488fb..aebddc1ca26add21c9067c0ac2a1ea42f4e83d13 100644 (file)
@@ -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,
index 9a5976847bbe6c9183cf3f744b529f3b6a779cd9..2b19819693291a627b0e654fd49de140a1aee22e 100644 (file)
@@ -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();
   }
 }
index b4c848293c5a99754c3d689e08739263a1910a87..265292ef8acfa8e31e7e4865474732053063c373 100644 (file)
@@ -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();
   }
 }
index 03ee0d7d93ef89d8c68c34c392f6bd5f3437f564..10b36ac82e2e51e9c2fd553c0fb44a6e9c1cd666 100644 (file)
@@ -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<Self, Error> {
-    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,
+    })
   }
 }
index 5b068d9aed24efbcb1f20062a0a4b5d611bc4d94..1e4fe70ae8450653d373a41558ec03afe1bd5c74 100644 (file)
@@ -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,
 }
 
index 1841b15917ab100bce1201e37d2a4f8dae3ff976..c0d7cf7e61bf7814ea4bcf64614610d57cd41a05 100644 (file)
@@ -33,7 +33,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
     .await?
     .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?;
 
-  let protocols = if context.settings().federation.enabled {
+  let protocols = if site_view.local_site.federation_enabled {
     vec!["activitypub".to_string()]
   } else {
     vec![]
@@ -55,7 +55,7 @@ async fn node_info(context: web::Data<LemmyContext>) -> Result<HttpResponse, Err
       local_posts: site_view.counts.posts,
       local_comments: site_view.counts.comments,
     },
-    open_registrations: site_view.site.open_registration,
+    open_registrations: site_view.local_site.open_registration,
   };
 
   Ok(HttpResponse::Ok().json(json))
index 4c015675d11ab2d4382004063006ef10ab865f37..a13aad3ebeeb20077b30a3182d3382ad1a8f8669 100644 (file)
@@ -6,7 +6,7 @@ use lemmy_db_schema::{
   source::{community::Community, person::Person},
   traits::ApubActor,
 };
-use lemmy_utils::{error::LemmyError, location_info, settings::structs::Settings};
+use lemmy_utils::{error::LemmyError, location_info};
 use lemmy_websocket::LemmyContext;
 use serde::Deserialize;
 use url::Url;
@@ -16,13 +16,11 @@ struct Params {
   resource: String,
 }
 
-pub fn config(cfg: &mut web::ServiceConfig, settings: &Settings) {
-  if settings.federation.enabled {
-    cfg.route(
-      ".well-known/webfinger",
-      web::get().to(get_webfinger_response),
-    );
-  }
+pub fn config(cfg: &mut web::ServiceConfig) {
+  cfg.route(
+    ".well-known/webfinger",
+    web::get().to(get_webfinger_response),
+  );
 }
 
 /// Responds to webfinger requests of the following format. There isn't any real documentation for
index ff58c37781ff9e71a284348f8b29272215a9ad68..0c8926d65fc44afffcc733a5042fd4595accdd7a 100644 (file)
@@ -43,6 +43,7 @@ uuid = { version = "1.1.2", features = ["serde", "v4"] }
 html2text = "0.4.2"
 rosetta-i18n = "0.1.2"
 parking_lot = "0.12.1"
+typed-builder = "0.10.0"
 
 [build-dependencies]
 rosetta-build = "0.1.2"
index 79bdbb1d8fd5dc393a386f33f5744dc95ae361d6..48911b5cf03feec7c7ac37f4e7170b5e9bed0d58 100644 (file)
@@ -1,10 +1,11 @@
-use crate::{settings::structs::RateLimitConfig, utils::get_ip, IpAddr};
+use crate::{utils::get_ip, IpAddr};
 use actix_web::{
   dev::{Service, ServiceRequest, ServiceResponse, Transform},
   HttpResponse,
 };
 use futures::future::{ok, Ready};
 use rate_limiter::{RateLimitType, RateLimiter};
+use serde::{Deserialize, Serialize};
 use std::{
   future::Future,
   pin::Pin,
@@ -12,9 +13,50 @@ use std::{
   sync::{Arc, Mutex},
   task::{Context, Poll},
 };
+use typed_builder::TypedBuilder;
 
 pub mod rate_limiter;
 
+#[derive(Debug, Deserialize, Serialize, Clone, TypedBuilder)]
+pub struct RateLimitConfig {
+  #[builder(default = 180)]
+  /// Maximum number of messages created in interval
+  pub message: i32,
+  #[builder(default = 60)]
+  /// Interval length for message limit, in seconds
+  pub message_per_second: i32,
+  #[builder(default = 6)]
+  /// Maximum number of posts created in interval
+  pub post: i32,
+  #[builder(default = 300)]
+  /// Interval length for post limit, in seconds
+  pub post_per_second: i32,
+  #[builder(default = 3)]
+  /// Maximum number of registrations in interval
+  pub register: i32,
+  #[builder(default = 3600)]
+  /// Interval length for registration limit, in seconds
+  pub register_per_second: i32,
+  #[builder(default = 6)]
+  /// Maximum number of image uploads in interval
+  pub image: i32,
+  #[builder(default = 3600)]
+  /// Interval length for image uploads, in seconds
+  pub image_per_second: i32,
+  #[builder(default = 6)]
+  /// Maximum number of comments created in interval
+  pub comment: i32,
+  #[builder(default = 600)]
+  /// Interval length for comment limit, in seconds
+  pub comment_per_second: i32,
+  #[builder(default = 60)]
+  /// Maximum number of searches created in interval
+  pub search: i32,
+  #[builder(default = 600)]
+  /// Interval length for search limit, in seconds
+  pub search_per_second: i32,
+}
+
 #[derive(Debug, Clone)]
 pub struct RateLimit {
   // it might be reasonable to use a std::sync::Mutex here, since we don't need to lock this
index 50d3fc583dabf9d9635de821008d0f12ba086a1e..42d571662e8b4ecf5335be06ee46807840933b9c 100644 (file)
@@ -6,7 +6,7 @@ use crate::{
 use anyhow::{anyhow, Context};
 use deser_hjson::from_str;
 use once_cell::sync::Lazy;
-use regex::{Regex, RegexBuilder};
+use regex::Regex;
 use std::{env, fs, io::Error};
 
 pub mod structs;
@@ -90,15 +90,6 @@ impl Settings {
     WEBFINGER_REGEX.to_owned()
   }
 
-  pub fn slur_regex(&self) -> Option<Regex> {
-    self.slur_filter.as_ref().map(|slurs| {
-      RegexBuilder::new(slurs)
-        .case_insensitive(true)
-        .build()
-        .expect("compile regex")
-    })
-  }
-
   pub fn pictrs_config(&self) -> Result<PictrsConfig, LemmyError> {
     self
       .pictrs
index 28dcb5c59f9a9fffac88abb0b86679083073360f..d32f3dc26652502190680effc68eeacae1ffe79e 100644 (file)
@@ -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<RateLimitConfig>,
   /// Settings related to activitypub federation
-  #[default(Default::default())]
-  pub federation: FederationConfig,
   /// Pictrs image server configuration.
   #[default(Some(Default::default()))]
   pub(crate) pictrs: Option<PictrsConfig>,
-  #[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<String>,
-  /// 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<String>,
 }
 
-#[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<Vec<String>>,
-  /// Instances which we never federate anything with (but previously federated objects are unaffected)
-  #[default(None)]
-  pub blocked_instances: Option<Vec<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).
-  #[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
index e887bae4f4c3d4ff78252985877fef2a10875ed3..e7bfdb59d35dcab1104240c94ad7885ab2815f49 100644 (file)
@@ -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";
index 99dd21c8fd3bedae8ecbaf6a30eaa23689cf79f0..9d481de1906586be155faf47d9564bfd9fd4acc2 100644 (file)
@@ -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<Regex> = Lazy::new(|| {
@@ -60,6 +61,15 @@ pub(crate) fn slur_check<'a>(
   }
 }
 
+pub fn build_slur_regex(regex_str: Option<&str>) -> Option<Regex> {
+  regex_str.map(|slurs| {
+    RegexBuilder::new(slurs)
+      .case_insensitive(true)
+      .build()
+      .expect("compile regex")
+  })
+}
+
 pub fn check_slurs(text: &str, slur_regex: &Option<Regex>) -> 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<Option<String>>,
+  require_application: &Option<bool>,
+) -> 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<String, LemmyError> {
+  Ok(actor_id.host_str().context(location_info!())?.to_string())
+}
+
 #[cfg(test)]
 mod tests {
   use crate::utils::{clean_url_params, is_valid_post_title};
index f5d6f0eabafd559417bf8f203fd655f7858bffcf..454debaede4cc932ac15fea9bf620cf1daf1ae4c 160000 (submodule)
@@ -1 +1 @@
-Subproject commit f5d6f0eabafd559417bf8f203fd655f7858bffcf
+Subproject commit 454debaede4cc932ac15fea9bf620cf1daf1ae4c
index ce0f73995545612f59206a922191ba9cd2784ca7..59187d2cfad94a6c9033d9e135cda19c5062bb67 100644 (file)
@@ -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,
index 246c1f5cab37eb44d44adf641621ae976f8d3c9a..9622ead4b33c08e4ab32ecc7c02f30d2badfa584 100644 (file)
@@ -6,7 +6,6 @@
     admin_username: lemmy_alpha
     admin_password: lemmylemmy
     site_name: lemmy-alpha
-    sidebar: alphas sidebar
   }
   database: {
     database: lemmy
     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
-  }
 }
index 87184f1aecaa9d898258528f990a810ba160cd22..f199055dedece1ef93cc42ea234cb3d5520a93bc 100644 (file)
     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
-  }
 }
index 9c1c7aee716184c4e3eff1584f2939a918c0dae4..98f7606fc4a67ec363457365dd39418b592f20ba 100644 (file)
     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
-  }
 }
index c1b19f07f8b06d620ab6809caec7dd4d1b57e81c..f7c64e2ffee6fd949acdc304501ca21f4fa70973 100644 (file)
     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
-  }
 }
index 4d479174637218eb6fe2d734e2c689ef66085df0..632f10dfa29eac37d97ce5efd0e6c40d38c9018e 100644 (file)
     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 (file)
index 0000000..3ca49b5
--- /dev/null
@@ -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 (file)
index 0000000..e814ce2
--- /dev/null
@@ -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;
+
index 347d50cbba545a8806e5e04835cda497eed5993b..159514a6ccf97c3a5e85527e8dccd127ed74df79 100644 (file)
@@ -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(())
+}
index 3dcc41e58605043f7d7630406baf3ad045bc7511..ffac400f9a0dc93899a072a9ef8b77e2dfce28b4 100644 (file)
@@ -1,4 +1,3 @@
-#![recursion_limit = "512"]
 pub mod api_routes;
 pub mod code_migrations;
 pub mod root_span_builder;
index 3c0bf3f0206e1f718d4719d4d5dfbe4b0f5fcd37..9ef2f541d27fa943d7d9facd6a7b6f0a2aeede55 100644 (file)
@@ -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()