From 89e0cfd8741b6763ca04e90558bccf4c3c380cfa Mon Sep 17 00:00:00 2001 From: tobi <31960611+tsmethurst@users.noreply.github.com> Date: Sat, 13 Apr 2024 13:25:10 +0200 Subject: [feature] Admin accounts endpoints; approve/reject sign-ups (#2826) * update settings panels, add pending overview + approve/deny functions * add admin accounts get, approve, reject * send approved/rejected emails * use signup URL * docs! * email * swagger * web linting * fix email tests * wee lil fixerinos * use new paging logic for GetAccounts() series of admin endpoints, small changes to query building * shuffle useAccountIDIn check *before* adding to query * fix parse from toot react error * use `netip.Addr` * put valid slices in globals * optimistic updates for account state --------- Co-authored-by: kim --- web/source/settings/admin/accounts/detail.jsx | 112 --------- .../settings/admin/accounts/detail/actions.tsx | 89 ++++++++ .../admin/accounts/detail/handlesignup.tsx | 118 ++++++++++ .../settings/admin/accounts/detail/index.tsx | 179 +++++++++++++++ web/source/settings/admin/accounts/index.jsx | 138 ----------- web/source/settings/admin/accounts/index.tsx | 49 ++++ .../settings/admin/accounts/pending/index.tsx | 40 ++++ .../settings/admin/accounts/search/index.tsx | 125 ++++++++++ .../settings/admin/actions/keys/expireremote.jsx | 59 ----- .../settings/admin/actions/keys/expireremote.tsx | 63 ++++++ web/source/settings/admin/actions/keys/index.jsx | 30 --- web/source/settings/admin/actions/keys/index.tsx | 30 +++ .../settings/admin/actions/media/cleanup.jsx | 57 ----- .../settings/admin/actions/media/cleanup.tsx | 61 +++++ web/source/settings/admin/actions/media/index.jsx | 30 --- web/source/settings/admin/actions/media/index.tsx | 30 +++ .../settings/admin/domain-permissions/form.tsx | 9 +- web/source/settings/admin/emoji/local/detail.js | 36 ++- web/source/settings/admin/emoji/local/index.js | 35 --- web/source/settings/admin/emoji/local/index.tsx | 35 +++ web/source/settings/admin/emoji/local/new-emoji.js | 112 --------- .../settings/admin/emoji/local/new-emoji.tsx | 116 ++++++++++ web/source/settings/admin/emoji/local/overview.js | 2 +- web/source/settings/admin/emoji/remote/index.js | 54 ----- web/source/settings/admin/emoji/remote/index.tsx | 54 +++++ .../settings/admin/emoji/remote/parse-from-toot.js | 226 ------------------ .../admin/emoji/remote/parse-from-toot.tsx | 235 +++++++++++++++++++ web/source/settings/admin/reports/detail.jsx | 252 --------------------- web/source/settings/admin/reports/detail.tsx | 252 +++++++++++++++++++++ web/source/settings/admin/reports/index.jsx | 103 --------- web/source/settings/admin/reports/index.tsx | 103 +++++++++ web/source/settings/admin/reports/username.jsx | 54 ----- web/source/settings/admin/reports/username.tsx | 54 +++++ web/source/settings/admin/settings/rules.jsx | 167 -------------- web/source/settings/admin/settings/rules.tsx | 174 ++++++++++++++ web/source/settings/components/account-list.tsx | 82 +++++++ .../settings/components/form/mutation-button.jsx | 48 ---- .../settings/components/form/mutation-button.tsx | 72 ++++++ web/source/settings/index.js | 29 ++- web/source/settings/lib/navigation/util.js | 49 ---- web/source/settings/lib/navigation/util.ts | 54 +++++ web/source/settings/lib/query/admin/index.ts | 65 ++++-- .../settings/lib/query/admin/reports/index.ts | 2 +- web/source/settings/lib/types/account.ts | 88 +++++++ web/source/settings/style.css | 41 +++- 45 files changed, 2235 insertions(+), 1578 deletions(-) delete mode 100644 web/source/settings/admin/accounts/detail.jsx create mode 100644 web/source/settings/admin/accounts/detail/actions.tsx create mode 100644 web/source/settings/admin/accounts/detail/handlesignup.tsx create mode 100644 web/source/settings/admin/accounts/detail/index.tsx delete mode 100644 web/source/settings/admin/accounts/index.jsx create mode 100644 web/source/settings/admin/accounts/index.tsx create mode 100644 web/source/settings/admin/accounts/pending/index.tsx create mode 100644 web/source/settings/admin/accounts/search/index.tsx delete mode 100644 web/source/settings/admin/actions/keys/expireremote.jsx create mode 100644 web/source/settings/admin/actions/keys/expireremote.tsx delete mode 100644 web/source/settings/admin/actions/keys/index.jsx create mode 100644 web/source/settings/admin/actions/keys/index.tsx delete mode 100644 web/source/settings/admin/actions/media/cleanup.jsx create mode 100644 web/source/settings/admin/actions/media/cleanup.tsx delete mode 100644 web/source/settings/admin/actions/media/index.jsx create mode 100644 web/source/settings/admin/actions/media/index.tsx delete mode 100644 web/source/settings/admin/emoji/local/index.js create mode 100644 web/source/settings/admin/emoji/local/index.tsx delete mode 100644 web/source/settings/admin/emoji/local/new-emoji.js create mode 100644 web/source/settings/admin/emoji/local/new-emoji.tsx delete mode 100644 web/source/settings/admin/emoji/remote/index.js create mode 100644 web/source/settings/admin/emoji/remote/index.tsx delete mode 100644 web/source/settings/admin/emoji/remote/parse-from-toot.js create mode 100644 web/source/settings/admin/emoji/remote/parse-from-toot.tsx delete mode 100644 web/source/settings/admin/reports/detail.jsx create mode 100644 web/source/settings/admin/reports/detail.tsx delete mode 100644 web/source/settings/admin/reports/index.jsx create mode 100644 web/source/settings/admin/reports/index.tsx delete mode 100644 web/source/settings/admin/reports/username.jsx create mode 100644 web/source/settings/admin/reports/username.tsx delete mode 100644 web/source/settings/admin/settings/rules.jsx create mode 100644 web/source/settings/admin/settings/rules.tsx create mode 100644 web/source/settings/components/account-list.tsx delete mode 100644 web/source/settings/components/form/mutation-button.jsx create mode 100644 web/source/settings/components/form/mutation-button.tsx delete mode 100644 web/source/settings/lib/navigation/util.js create mode 100644 web/source/settings/lib/navigation/util.ts create mode 100644 web/source/settings/lib/types/account.ts (limited to 'web/source/settings') diff --git a/web/source/settings/admin/accounts/detail.jsx b/web/source/settings/admin/accounts/detail.jsx deleted file mode 100644 index 63049c149..000000000 --- a/web/source/settings/admin/accounts/detail.jsx +++ /dev/null @@ -1,112 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -const React = require("react"); -const { useRoute, Redirect } = require("wouter"); - -const query = require("../../lib/query"); - -const FormWithData = require("../../lib/form/form-with-data").default; - -const { useBaseUrl } = require("../../lib/navigation/util"); -const FakeProfile = require("../../components/fake-profile"); -const MutationButton = require("../../components/form/mutation-button"); - -const useFormSubmit = require("../../lib/form/submit").default; -const { useValue, useTextInput } = require("../../lib/form"); -const { TextInput } = require("../../components/form/inputs"); - -module.exports = function AccountDetail({ }) { - const baseUrl = useBaseUrl(); - - let [_match, params] = useRoute(`${baseUrl}/:accountId`); - - if (params?.accountId == undefined) { - return ; - } else { - return ( -
-

- Account Details -

- -
- ); - } -}; - -function AccountDetailForm({ data: account }) { - let content; - if (account.suspended) { - content = ( -

Account is suspended.

- ); - } else { - content = ; - } - - return ( - <> - - - {content} - - ); -} - -function ModifyAccount({ account }) { - const form = { - id: useValue("id", account.id), - reason: useTextInput("text") - }; - - const [modifyAccount, result] = useFormSubmit(form, query.useActionAccountMutation()); - - return ( -
-

Actions

- - -
- {/* - */} - -
- - ); -} \ No newline at end of file diff --git a/web/source/settings/admin/accounts/detail/actions.tsx b/web/source/settings/admin/accounts/detail/actions.tsx new file mode 100644 index 000000000..75ab8db6e --- /dev/null +++ b/web/source/settings/admin/accounts/detail/actions.tsx @@ -0,0 +1,89 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import React from "react"; + +import { useActionAccountMutation } from "../../../lib/query"; + +import MutationButton from "../../../components/form/mutation-button"; + +import useFormSubmit from "../../../lib/form/submit"; +import { + useValue, + useTextInput, + useBoolInput, +} from "../../../lib/form"; + +import { Checkbox, TextInput } from "../../../components/form/inputs"; +import { AdminAccount } from "../../../lib/types/account"; + +export interface AccountActionsProps { + account: AdminAccount, +} + +export function AccountActions({ account }: AccountActionsProps) { + const form = { + id: useValue("id", account.id), + reason: useTextInput("text") + }; + + const reallySuspend = useBoolInput("reallySuspend"); + const [accountAction, result] = useFormSubmit(form, useActionAccountMutation()); + + return ( +
+

Account Moderation Actions

+
+ Currently only the "suspend" action is implemented.
+ Suspending an account will delete it from your server, and remove all of its media, posts, relationships, etc.
+ If the suspended account is local, suspending will also send out a "delete" message to other servers, requesting them to remove its data from their instance as well.
+ Account suspension cannot be reversed. +
+ +
+ {/* + */} + + +
+ + ); +} diff --git a/web/source/settings/admin/accounts/detail/handlesignup.tsx b/web/source/settings/admin/accounts/detail/handlesignup.tsx new file mode 100644 index 000000000..a61145a22 --- /dev/null +++ b/web/source/settings/admin/accounts/detail/handlesignup.tsx @@ -0,0 +1,118 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import React from "react"; +import { useLocation } from "wouter"; + +import { useHandleSignupMutation } from "../../../lib/query"; + +import MutationButton from "../../../components/form/mutation-button"; + +import useFormSubmit from "../../../lib/form/submit"; +import { + useValue, + useTextInput, + useBoolInput, +} from "../../../lib/form"; + +import { Checkbox, Select, TextInput } from "../../../components/form/inputs"; +import { AdminAccount } from "../../../lib/types/account"; + +export interface HandleSignupProps { + account: AdminAccount, + accountsBaseUrl: string, +} + +export function HandleSignup({account, accountsBaseUrl}: HandleSignupProps) { + const form = { + id: useValue("id", account.id), + approveOrReject: useTextInput("approve_or_reject", { defaultValue: "approve" }), + privateComment: useTextInput("private_comment"), + message: useTextInput("message"), + sendEmail: useBoolInput("send_email"), + }; + + const [_location, setLocation] = useLocation(); + + const [handleSignup, result] = useFormSubmit(form, useHandleSignupMutation(), { + changedOnly: false, + // After submitting the form, redirect back to + // /settings/admin/accounts if rejecting, since + // account will no longer be available at + // /settings/admin/accounts/:accountID endpoint. + onFinish: (res) => { + if (form.approveOrReject.value === "approve") { + // An approve request: + // stay on this page and + // serve updated details. + return; + } + + if (res.data) { + // "reject" successful, + // redirect to accounts page. + setLocation(accountsBaseUrl); + } + } + }); + + return ( +
+

Handle Account Sign-Up

+ + { form.approveOrReject.value === "reject" && + // Only show form fields relevant + // to "reject" if rejecting. + // On "approve" these fields will + // be ignored anyway. + <> + + + + } + + + ); +} diff --git a/web/source/settings/admin/accounts/detail/index.tsx b/web/source/settings/admin/accounts/detail/index.tsx new file mode 100644 index 000000000..79eb493de --- /dev/null +++ b/web/source/settings/admin/accounts/detail/index.tsx @@ -0,0 +1,179 @@ +/* + GoToSocial + Copyright (C) GoToSocial Authors admin@gotosocial.org + SPDX-License-Identifier: AGPL-3.0-or-later + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ + +import React from "react"; +import { useRoute, Redirect } from "wouter"; + +import { useGetAccountQuery } from "../../../lib/query"; + +import FormWithData from "../../../lib/form/form-with-data"; + +import { useBaseUrl } from "../../../lib/navigation/util"; +import FakeProfile from "../../../components/fake-profile"; + +import { AdminAccount } from "../../../lib/types/account"; +import { HandleSignup } from "./handlesignup"; +import { AccountActions } from "./actions"; +import BackButton from "../../../components/back-button"; + +export default function AccountDetail() { + // /settings/admin/accounts + const accountsBaseUrl = useBaseUrl(); + + let [_match, params] = useRoute(`${accountsBaseUrl}/:accountId`); + + if (params?.accountId == undefined) { + return ; + } else { + return ( +
+

+ Account Details +

+ +
+ ); + } +} + +interface AccountDetailFormProps { + accountsBaseUrl: string, + data: AdminAccount, +} + +function AccountDetailForm({ data: adminAcct, accountsBaseUrl }: AccountDetailFormProps) { + let yesOrNo = (b: boolean) => { + return b ? "yes" : "no"; + }; + + let created = new Date(adminAcct.created_at).toDateString(); + let lastPosted = "never"; + if (adminAcct.account.last_status_at) { + lastPosted = new Date(adminAcct.account.last_status_at).toDateString(); + } + const local = !adminAcct.domain; + + return ( + <> + +

General Account Details

+ { adminAcct.suspended && +
+ + Account is suspended. +
+ } +
+ { !local && +
+
Domain
+
{adminAcct.domain}
+
} +
+
Created
+
+
+
+
Last posted
+
{lastPosted}
+
+
+
Suspended
+
{yesOrNo(adminAcct.suspended)}
+
+
+
Silenced
+
{yesOrNo(adminAcct.silenced)}
+
+
+
Statuses
+
{adminAcct.account.statuses_count}
+
+
+
Followers
+
{adminAcct.account.followers_count}
+
+
+
Following
+
{adminAcct.account.following_count}
+
+
+ { local && + // Only show local account details + // if this is a local account! + <> +

Local Account Details

+ { !adminAcct.approved && +
+ + Account is pending. +
+ } + { !adminAcct.confirmed && +
+ + Account email not yet confirmed. +
+ } +
+
+
Email
+
{adminAcct.email} {{adminAcct.confirmed ? "(confirmed)" : "(not confirmed)"} }
+
+
+
Disabled
+
{yesOrNo(adminAcct.disabled)}
+
+
+
Approved
+
{yesOrNo(adminAcct.approved)}
+
+
+
Sign-Up Reason
+
{adminAcct.invite_request ?? none provided}
+
+ { (adminAcct.ip && adminAcct.ip !== "0.0.0.0") && +
+
Sign-Up IP
+
{adminAcct.ip}
+
} + { adminAcct.locale && +
+
Locale
+
{adminAcct.locale}
+
} +
+ } + { local && !adminAcct.approved + ? + + : + + } + + ); +} diff --git a/web/source/settings/admin/accounts/index.jsx b/web/source/settings/admin/accounts/index.jsx deleted file mode 100644 index c642d903e..000000000 --- a/web/source/settings/admin/accounts/index.jsx +++ /dev/null @@ -1,138 +0,0 @@ -/* - GoToSocial - Copyright (C) GoToSocial Authors admin@gotosocial.org - SPDX-License-Identifier: AGPL-3.0-or-later - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ - -const React = require("react"); -const { Switch, Route, Link } = require("wouter"); - -const query = require("../../lib/query"); -const { useTextInput } = require("../../lib/form"); - -const AccountDetail = require("./detail"); -const { useBaseUrl } = require("../../lib/navigation/util"); -const { Error } = require("../../components/error"); - -module.exports = function Accounts({ baseUrl }) { - return ( -
- - - - - - -
- ); -}; - -function AccountOverview({ }) { - return ( - <> -

Accounts

-
- Pending #581, - there is currently no way to list accounts.
- You can perform actions on reported accounts by clicking their name in the report, or searching for a username below. -
- - - - ); -} - -function AccountSearchForm() { - const [searchAccount, result] = query.useSearchAccountMutation(); - - const [onAccountChange, _resetAccount, { account }] = useTextInput("account"); - - function submitSearch(e) { - e.preventDefault(); - if (account.trim().length != 0) { - searchAccount(account); - } - } - - return ( -
-
-
- -
- -