mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[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:
@ -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
|
||||
};
|
@ -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,
|
||||
|
@ -36,7 +36,7 @@ const extended = gtsApi.injectEndpoints({
|
||||
...params
|
||||
}
|
||||
}),
|
||||
providesTags: ["Reports"]
|
||||
providesTags: [{ type: "Reports", id: "LIST" }]
|
||||
}),
|
||||
|
||||
getReport: build.query<AdminReport, string>({
|
||||
|
88
web/source/settings/lib/types/account.ts
Normal file
88
web/source/settings/lib/types/account.ts
Normal 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,
|
||||
}
|
Reference in New Issue
Block a user