mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[feature/frontend] Let admins send test email to validate SMTP config (#2934)
* [feature/frontend] Let admins send test email to validate SMTP config * wee
This commit is contained in:
73
web/source/settings/lib/query/admin/actions/index.ts
Normal file
73
web/source/settings/lib/query/admin/actions/index.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
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 { gtsApi } from "../../gts-api";
|
||||
|
||||
const extended = gtsApi.injectEndpoints({
|
||||
endpoints: (build) => ({
|
||||
mediaCleanup: build.mutation({
|
||||
query: (days) => ({
|
||||
method: "POST",
|
||||
url: `/api/v1/admin/media_cleanup`,
|
||||
params: {
|
||||
remote_cache_days: days
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
instanceKeysExpire: build.mutation({
|
||||
query: (domain) => ({
|
||||
method: "POST",
|
||||
url: `/api/v1/admin/domain_keys_expire`,
|
||||
params: {
|
||||
domain: domain
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
sendTestEmail: build.mutation<any, { email: string, message?: string }>({
|
||||
query: (params) => ({
|
||||
method: "POST",
|
||||
url: `/api/v1/admin/email/test`,
|
||||
params: params,
|
||||
})
|
||||
}),
|
||||
}),
|
||||
});
|
||||
|
||||
/**
|
||||
* POST to /api/v1/admin/media_cleanup to trigger manual cleanup.
|
||||
*/
|
||||
const useMediaCleanupMutation = extended.useMediaCleanupMutation;
|
||||
|
||||
/**
|
||||
* POST to /api/v1/admin/domain_keys_expire to expire domain keys for the given domain.
|
||||
*/
|
||||
const useInstanceKeysExpireMutation = extended.useInstanceKeysExpireMutation;
|
||||
|
||||
/**
|
||||
* POST to /api/v1/admin/email/test to send a test email to the given address.
|
||||
*/
|
||||
const useSendTestEmailMutation = extended.useSendTestEmailMutation;
|
||||
|
||||
export {
|
||||
useMediaCleanupMutation,
|
||||
useInstanceKeysExpireMutation,
|
||||
useSendTestEmailMutation,
|
||||
};
|
@@ -37,26 +37,6 @@ const extended = gtsApi.injectEndpoints({
|
||||
...replaceCacheOnMutation("instanceV1"),
|
||||
}),
|
||||
|
||||
mediaCleanup: build.mutation({
|
||||
query: (days) => ({
|
||||
method: "POST",
|
||||
url: `/api/v1/admin/media_cleanup`,
|
||||
params: {
|
||||
remote_cache_days: days
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
instanceKeysExpire: build.mutation({
|
||||
query: (domain) => ({
|
||||
method: "POST",
|
||||
url: `/api/v1/admin/domain_keys_expire`,
|
||||
params: {
|
||||
domain: domain
|
||||
}
|
||||
})
|
||||
}),
|
||||
|
||||
getAccount: build.query<AdminAccount, string>({
|
||||
query: (id) => ({
|
||||
url: `/api/v1/admin/accounts/${id}`
|
||||
@@ -214,8 +194,6 @@ const extended = gtsApi.injectEndpoints({
|
||||
|
||||
export const {
|
||||
useUpdateInstanceMutation,
|
||||
useMediaCleanupMutation,
|
||||
useInstanceKeysExpireMutation,
|
||||
useGetAccountQuery,
|
||||
useLazyGetAccountQuery,
|
||||
useActionAccountMutation,
|
||||
|
29
web/source/settings/views/admin/actions/email/index.tsx
Normal file
29
web/source/settings/views/admin/actions/email/index.tsx
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
import Test from "./test";
|
||||
|
||||
export default function Email() {
|
||||
return (
|
||||
<div className="admin-actions-email">
|
||||
<Test />
|
||||
</div>
|
||||
);
|
||||
}
|
77
web/source/settings/views/admin/actions/email/test.tsx
Normal file
77
web/source/settings/views/admin/actions/email/test.tsx
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
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 React from "react";
|
||||
import { TextInput } from "../../../../components/form/inputs";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
import { useTextInput } from "../../../../lib/form";
|
||||
import { useSendTestEmailMutation } from "../../../../lib/query/admin/actions";
|
||||
import { useInstanceV1Query } from "../../../../lib/query/gts-api";
|
||||
import useFormSubmit from "../../../../lib/form/submit";
|
||||
|
||||
export default function Test({}) {
|
||||
const { data: instance } = useInstanceV1Query();
|
||||
|
||||
const form = {
|
||||
email: useTextInput("email", { defaultValue: instance?.email }),
|
||||
message: useTextInput("message")
|
||||
};
|
||||
|
||||
const [submit, result] = useFormSubmit(form, useSendTestEmailMutation(), { changedOnly: false });
|
||||
|
||||
return (
|
||||
<form onSubmit={submit}>
|
||||
<div className="form-section-docs">
|
||||
<h2>Send test email</h2>
|
||||
<p>
|
||||
To check whether your instance email configuration is correct, you can
|
||||
try sending a test email to the given address, with an optional message.
|
||||
<br/>
|
||||
If you do not have SMTP configured for your instance, this will do nothing.
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.gotosocial.org/en/latest/configuration/smtp/"
|
||||
target="_blank"
|
||||
className="docslink"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more about SMTP configuration (opens in a new tab)
|
||||
</a>
|
||||
</div>
|
||||
<TextInput
|
||||
field={form.email}
|
||||
label="Email"
|
||||
placeholder="someone@example.org"
|
||||
// Get email validation for free.
|
||||
type="email"
|
||||
required={true}
|
||||
/>
|
||||
<TextInput
|
||||
field={form.message}
|
||||
label="Message (optional)"
|
||||
placeholder="Please disregard this test email, thanks!"
|
||||
/>
|
||||
<MutationButton
|
||||
disabled={!form.email.value}
|
||||
label="Send"
|
||||
result={result}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
@@ -21,7 +21,7 @@ import React from "react";
|
||||
import { TextInput } from "../../../../components/form/inputs";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
import { useTextInput } from "../../../../lib/form";
|
||||
import { useInstanceKeysExpireMutation } from "../../../../lib/query/admin";
|
||||
import { useInstanceKeysExpireMutation } from "../../../../lib/query/admin/actions";
|
||||
|
||||
export default function ExpireRemote({}) {
|
||||
const domainField = useTextInput("domain");
|
||||
@@ -35,15 +35,20 @@ export default function ExpireRemote({}) {
|
||||
|
||||
return (
|
||||
<form onSubmit={submitExpire}>
|
||||
<h2>Expire remote instance keys</h2>
|
||||
<p>
|
||||
Mark all public keys from the given remote instance as expired.<br/><br/>
|
||||
This is useful in cases where the remote domain has had to rotate their keys for whatever
|
||||
reason (security issue, data leak, routine safety procedure, etc), and your instance can no
|
||||
longer communicate with theirs properly using cached keys. A key marked as expired in this way
|
||||
will be lazily refetched next time a request is made to your instance signed by the owner of that
|
||||
key.
|
||||
</p>
|
||||
<div className="form-section-docs">
|
||||
<h2>Expire remote instance keys</h2>
|
||||
<p>
|
||||
Mark all public keys from the given remote instance as expired.
|
||||
<br/>
|
||||
This is useful in cases where the remote domain has had to rotate
|
||||
their keys for whatever reason (security issue, data leak, routine
|
||||
safety procedure, etc), and your instance can no longer communicate
|
||||
with theirs properly using cached keys.
|
||||
<br/>
|
||||
A key marked as expired in this way will be lazily refetched next time
|
||||
a request is made to your instance signed by the owner of that key.
|
||||
</p>
|
||||
</div>
|
||||
<TextInput
|
||||
field={domainField}
|
||||
label="Domain"
|
||||
|
@@ -22,9 +22,8 @@ import ExpireRemote from "./expireremote";
|
||||
|
||||
export default function Keys() {
|
||||
return (
|
||||
<>
|
||||
<h1>Key Actions</h1>
|
||||
<div className="admin-actions-keys">
|
||||
<ExpireRemote />
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -22,10 +22,10 @@ import React from "react";
|
||||
import { useTextInput } from "../../../../lib/form";
|
||||
import { TextInput } from "../../../../components/form/inputs";
|
||||
import MutationButton from "../../../../components/form/mutation-button";
|
||||
import { useMediaCleanupMutation } from "../../../../lib/query/admin";
|
||||
import { useMediaCleanupMutation } from "../../../../lib/query/admin/actions";
|
||||
|
||||
export default function Cleanup({}) {
|
||||
const daysField = useTextInput("days", { defaultValue: "30" });
|
||||
const daysField = useTextInput("days", { defaultValue: "7" });
|
||||
|
||||
const [mediaCleanup, mediaCleanupResult] = useMediaCleanupMutation();
|
||||
|
||||
@@ -36,12 +36,24 @@ export default function Cleanup({}) {
|
||||
|
||||
return (
|
||||
<form onSubmit={submitCleanup}>
|
||||
<h2>Cleanup</h2>
|
||||
<p>
|
||||
<div className="form-section-docs">
|
||||
<h2>Cleanup</h2>
|
||||
<p>
|
||||
Clean up remote media older than the specified number of days.
|
||||
<br/>
|
||||
If the remote instance is still online they will be refetched when needed.
|
||||
<br/>
|
||||
Also cleans up unused headers and avatars from the media cache.
|
||||
</p>
|
||||
</p>
|
||||
<a
|
||||
href="https://docs.gotosocial.org/en/latest/admin/media_caching/"
|
||||
target="_blank"
|
||||
className="docslink"
|
||||
rel="noreferrer"
|
||||
>
|
||||
Learn more about media caching + cleanup (opens in a new tab)
|
||||
</a>
|
||||
</div>
|
||||
<TextInput
|
||||
field={daysField}
|
||||
label="Days"
|
||||
|
@@ -22,9 +22,8 @@ import Cleanup from "./cleanup";
|
||||
|
||||
export default function Media() {
|
||||
return (
|
||||
<>
|
||||
<h1>Media Actions</h1>
|
||||
<div className="admin-actions-media">
|
||||
<Cleanup />
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ import { useHasPermission } from "../../lib/navigation/util";
|
||||
* - /settings/admin/emojis/local/:emojiId
|
||||
* - /settings/admin/emojis/remote
|
||||
* - /settings/admin/actions
|
||||
* - /settings/admin/actions/email
|
||||
* - /settings/admin/actions/media
|
||||
* - /settings/admin/actions/keys
|
||||
* - /settings/admin/http-header-permissions/blocks
|
||||
@@ -94,9 +95,14 @@ function AdminActionsMenu() {
|
||||
<MenuItem
|
||||
name="Actions"
|
||||
itemUrl="actions"
|
||||
defaultChild="media"
|
||||
defaultChild="email"
|
||||
icon="fa-bolt"
|
||||
>
|
||||
<MenuItem
|
||||
name="Email"
|
||||
itemUrl="email"
|
||||
icon="fa-email-bulk"
|
||||
/>
|
||||
<MenuItem
|
||||
name="Media"
|
||||
itemUrl="media"
|
||||
|
@@ -31,6 +31,7 @@ import EmojiDetail from "./emoji/local/detail";
|
||||
import RemoteEmoji from "./emoji/remote";
|
||||
import HeaderPermsOverview from "./http-header-permissions/overview";
|
||||
import HeaderPermDetail from "./http-header-permissions/detail";
|
||||
import Email from "./actions/email";
|
||||
|
||||
/*
|
||||
EXPORTED COMPONENTS
|
||||
@@ -47,6 +48,7 @@ import HeaderPermDetail from "./http-header-permissions/detail";
|
||||
* - /settings/admin/actions
|
||||
* - /settings/admin/actions/media
|
||||
* - /settings/admin/actions/keys
|
||||
* - /settings/admin/actions/email
|
||||
* - /settings/admin/http-header-permissions/allows
|
||||
* - /settings/admin/http-header-permissions/allows/:allowId
|
||||
* - /settings/admin/http-header-permissions/blocks
|
||||
@@ -108,6 +110,7 @@ function AdminEmojisRouter() {
|
||||
|
||||
/**
|
||||
* - /settings/admin/actions
|
||||
* - /settings/admin/actions/email
|
||||
* - /settings/admin/actions/media
|
||||
* - /settings/admin/actions/keys
|
||||
*/
|
||||
@@ -121,9 +124,10 @@ function AdminActionsRouter() {
|
||||
<Router base={thisBase}>
|
||||
<ErrorBoundary>
|
||||
<Switch>
|
||||
<Route path="/email" component={Email} />
|
||||
<Route path="/media" component={Media} />
|
||||
<Route path="/keys" component={Keys} />
|
||||
<Route><Redirect to="/media" /></Route>
|
||||
<Route><Redirect to="/email" /></Route>
|
||||
</Switch>
|
||||
</ErrorBoundary>
|
||||
</Router>
|
||||
|
Reference in New Issue
Block a user