0
Fork 0
mirror of https://github.com/logto-io/logto.git synced 2024-12-16 20:26:19 -05:00

feat(console): regenerate signing key (#3866)

This commit is contained in:
Xiao Yijun 2023-05-22 10:49:49 +08:00 committed by GitHub
parent 9423b273b6
commit 85fb03651c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 111 additions and 1 deletions

View file

@ -0,0 +1,3 @@
<svg width="17" height="17" viewBox="0 0 17 17" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M14.5 7.8335C14.1 7.8335 13.8333 8.10016 13.8333 8.50016C13.8333 10.4335 12.8333 12.1668 11.1667 13.1002C8.63334 14.5668 5.36668 13.7002 3.90001 11.1668C2.43334 8.6335 3.30001 5.36683 5.83334 3.90016C8.03334 2.6335 10.7 3.10016 12.3667 4.8335H10.7667C10.3667 4.8335 10.1 5.10016 10.1 5.50016C10.1 5.90016 10.3667 6.16683 10.7667 6.16683H13.7667C14.1667 6.16683 14.4333 5.90016 14.4333 5.50016V2.50016C14.4333 2.10016 14.1667 1.8335 13.7667 1.8335C13.3667 1.8335 13.1 2.10016 13.1 2.50016V3.70016C11.8333 2.50016 10.2333 1.8335 8.50001 1.8335C4.83334 1.8335 1.83334 4.8335 1.83334 8.50016C1.83334 12.1668 4.83334 15.1668 8.50001 15.1668C12.1667 15.1668 15.1667 12.1668 15.1667 8.50016C15.1667 8.10016 14.9 7.8335 14.5 7.8335Z" fill="currentColor"/>
</svg>

After

Width:  |  Height:  |  Size: 860 B

View file

@ -0,0 +1,9 @@
@use '@/scss/underscore' as _;
.regenerateButton {
margin-top: _.unit(3);
}
.signingKeyField {
width: 100%;
}

View file

@ -0,0 +1,91 @@
import { type Hook } from '@logto/schemas';
import { useCallback, useEffect, useState } from 'react';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import Redo from '@/assets/images/redo.svg';
import Button from '@/components/Button';
import ConfirmModal from '@/components/ConfirmModal';
import CopyToClipboard from '@/components/CopyToClipboard';
import DynamicT from '@/components/DynamicT';
import FormField from '@/components/FormField';
import useApi from '@/hooks/use-api';
import * as styles from './index.module.scss';
type Props = {
hookId: string;
signingKey: string;
onSigningKeyUpdated: (signingKey: string) => void;
};
function SigningKeyField({ hookId, signingKey, onSigningKeyUpdated }: Props) {
const { t } = useTranslation(undefined);
const api = useApi();
const [isRegenerateFormOpen, setIsRegenerateFormOpen] = useState(false);
const [isRegenerating, setIsRegenerating] = useState(false);
const regenerateSigningKey = useCallback(
async (silent = false) => {
if (isRegenerating) {
return;
}
setIsRegenerating(true);
try {
const { signingKey } = await api.patch(`api/hooks/${hookId}/signing-key`).json<Hook>();
if (!silent) {
toast.success(t('admin_console.webhook_details.settings.regenerated'));
}
setIsRegenerateFormOpen(false);
onSigningKeyUpdated(signingKey);
} finally {
setIsRegenerating(false);
}
},
[api, hookId, isRegenerating, onSigningKeyUpdated, t]
);
useEffect(() => {
if (!signingKey) {
void regenerateSigningKey(true);
}
}, [regenerateSigningKey, signingKey]);
return (
<FormField
title="webhook_details.settings.signing_key"
tip={<DynamicT forKey="webhook_details.settings.signing_key_tip" />}
>
<CopyToClipboard
hasVisibilityToggle
value={signingKey}
variant="border"
className={styles.signingKeyField}
/>
<Button
type="text"
size="small"
icon={<Redo />}
title="webhook_details.settings.regenerate"
className={styles.regenerateButton}
onClick={() => {
setIsRegenerateFormOpen(true);
}}
/>
<ConfirmModal
isOpen={isRegenerateFormOpen}
isLoading={isRegenerating}
confirmButtonText="webhook_details.settings.regenerate"
title="webhook_details.settings.regenerate_key_title"
onCancel={async () => {
setIsRegenerateFormOpen(false);
}}
onConfirm={async () => regenerateSigningKey()}
>
<DynamicT forKey="webhook_details.settings.regenerate_key_reminder" />
</ConfirmModal>
</FormField>
);
}
export default SigningKeyField;

View file

@ -1,4 +1,5 @@
import { type Hook } from '@logto/schemas';
import { useState } from 'react';
import { FormProvider, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
@ -14,11 +15,12 @@ import { type WebhookDetailsFormType, type WebhookDetailsOutletContext } from '.
import { webhookDetailsParser } from '../utils';
import CustomHeaderField from './components/CustomHeaderField';
import SigningKeyField from './components/SigningKeyField';
function WebhookSettings() {
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
const { hook, isDeleting, onHookUpdated } = useOutletContext<WebhookDetailsOutletContext>();
const [signingKey, setSigningKey] = useState(hook.signingKey);
const webhookFormData = webhookDetailsParser.toLocalForm(hook);
const formMethods = useForm<WebhookDetailsFormType>({
defaultValues: webhookFormData,
@ -54,6 +56,11 @@ function WebhookSettings() {
>
<FormProvider {...formMethods}>
<BasicWebhookForm />
<SigningKeyField
hookId={hook.id}
signingKey={signingKey}
onSigningKeyUpdated={setSigningKey}
/>
<CustomHeaderField />
</FormProvider>
</FormCard>