[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 <grufwub@gmail.com>
This commit is contained in:
tobi
2024-04-13 13:25:10 +02:00
committed by GitHub
parent 1439042104
commit 89e0cfd874
74 changed files with 4102 additions and 545 deletions

View File

@ -17,16 +17,16 @@
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const React = require("react");
const RoleContext = React.createContext([]);
const BaseUrlContext = React.createContext(null);
import { createContext, useContext } from "react";
const RoleContext = createContext([]);
const BaseUrlContext = createContext<string>("");
function urlSafe(str) {
return str.toLowerCase().replace(/[\s/]+/g, "-");
}
function useHasPermission(permissions) {
const roles = React.useContext(RoleContext);
const roles = useContext(RoleContext);
return checkPermission(permissions, roles);
}
@ -41,9 +41,14 @@ function checkPermission(requiredPermissisons, user) {
}
function useBaseUrl() {
return React.useContext(BaseUrlContext);
return useContext(BaseUrlContext);
}
module.exports = {
urlSafe, RoleContext, useHasPermission, checkPermission, BaseUrlContext, useBaseUrl
};
export {
urlSafe,
RoleContext,
useHasPermission,
checkPermission,
BaseUrlContext,
useBaseUrl
};

View File

@ -20,6 +20,7 @@
import { replaceCacheOnMutation, removeFromCacheOnMutation } from "../query-modifiers";
import { gtsApi } from "../gts-api";
import { listToKeyedObject } from "../transforms";
import { AdminAccount, HandleSignupParams, SearchAccountParams } from "../../types/account";
const extended = gtsApi.injectEndpoints({
endpoints: (build) => ({
@ -54,14 +55,43 @@ const extended = gtsApi.injectEndpoints({
})
}),
getAccount: build.query({
getAccount: build.query<AdminAccount, string>({
query: (id) => ({
url: `/api/v1/accounts/${id}`
url: `/api/v1/admin/accounts/${id}`
}),
providesTags: (_, __, id) => [{ type: "Account", id }]
providesTags: (_result, _error, id) => [
{ type: 'Account', id }
],
}),
actionAccount: build.mutation({
searchAccounts: build.query<AdminAccount[], SearchAccountParams>({
query: (form) => {
const params = new(URLSearchParams);
Object.entries(form).forEach(([k, v]) => {
if (v !== undefined) {
params.append(k, v);
}
});
let query = "";
if (params.size !== 0) {
query = `?${params.toString()}`;
}
return {
url: `/api/v2/admin/accounts${query}`
};
},
providesTags: (res) =>
res
? [
...res.map(({ id }) => ({ type: 'Account' as const, id })),
{ type: 'Account', id: 'LIST' },
]
: [{ type: 'Account', id: 'LIST' }],
}),
actionAccount: build.mutation<string, { id: string, action: string, reason: string }>({
query: ({ id, action, reason }) => ({
method: "POST",
url: `/api/v1/admin/accounts/${id}/action`,
@ -71,16 +101,23 @@ const extended = gtsApi.injectEndpoints({
text: reason
}
}),
invalidatesTags: (_, __, { id }) => [{ type: "Account", id }]
invalidatesTags: (_result, _error, { id }) => [
{ type: 'Account', id },
],
}),
searchAccount: build.mutation({
query: (username) => ({
url: `/api/v2/search?q=${encodeURIComponent(username)}&resolve=true`
}),
transformResponse: (res) => {
return res.accounts ?? [];
}
handleSignup: build.mutation<AdminAccount, HandleSignupParams>({
query: ({id, approve_or_reject, ...formData}) => {
return {
method: "POST",
url: `/api/v1/admin/accounts/${id}/${approve_or_reject}`,
asForm: true,
body: approve_or_reject === "reject" ?? formData,
};
},
invalidatesTags: (_result, _error, { id }) => [
{ type: 'Account', id },
],
}),
instanceRules: build.query({
@ -140,7 +177,9 @@ export const {
useInstanceKeysExpireMutation,
useGetAccountQuery,
useActionAccountMutation,
useSearchAccountMutation,
useSearchAccountsQuery,
useLazySearchAccountsQuery,
useHandleSignupMutation,
useInstanceRulesQuery,
useAddInstanceRuleMutation,
useUpdateInstanceRuleMutation,

View File

@ -36,7 +36,7 @@ const extended = gtsApi.injectEndpoints({
...params
}
}),
providesTags: ["Reports"]
providesTags: [{ type: "Reports", id: "LIST" }]
}),
getReport: build.query<AdminReport, string>({

View File

@ -0,0 +1,88 @@
/*
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 <http://www.gnu.org/licenses/>.
*/
import { CustomEmoji } from "./custom-emoji";
export interface AdminAccount {
id: string,
username: string,
domain: string | null,
created_at: string,
email: string,
ip: string | null,
ips: [],
locale: string,
invite_request: string | null,
role: any,
confirmed: boolean,
approved: boolean,
disabled: boolean,
silenced: boolean,
suspended: boolean,
created_by_application_id: string,
account: Account,
}
export interface Account {
id: string,
username: string,
acct: string,
display_name: string,
locked: boolean,
discoverable: boolean,
bot: boolean,
created_at: string,
note: string,
url: string,
avatar: string,
avatar_static: string,
header: string,
header_static: string,
followers_count: number,
following_count: number,
statuses_count: number,
last_status_at: string,
emojis: CustomEmoji[],
fields: [],
enable_rss: boolean,
role: any,
}
export interface SearchAccountParams {
origin?: "local" | "remote",
status?: "active" | "pending" | "disabled" | "silenced" | "suspended",
permissions?: "staff",
username?: string,
display_name?: string,
by_domain?: string,
email?: string,
ip?: string,
max_id?: string,
since_id?: string,
min_id?: string,
limit?: number,
}
export interface HandleSignupParams {
id: string,
approve_or_reject: "approve" | "reject",
private_comment?: string,
message?: string,
send_email?: boolean,
}