|
@@ -1,5 +1,4 @@
|
|
import React from 'react';
|
|
import React from 'react';
|
|
-import { trpc } from '@/utils/trpc';
|
|
|
|
import { Switch } from '@/components/ui/Switch';
|
|
import { Switch } from '@/components/ui/Switch';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { Button } from '@/components/ui/Button';
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/Dialog';
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/Dialog';
|
|
@@ -9,10 +8,13 @@ import { OtpInput } from '@/components/ui/OtpInput';
|
|
import { toast } from 'react-hot-toast';
|
|
import { toast } from 'react-hot-toast';
|
|
import { useDisclosure } from '@/client/hooks/useDisclosure';
|
|
import { useDisclosure } from '@/client/hooks/useDisclosure';
|
|
import { useTranslations } from 'next-intl';
|
|
import { useTranslations } from 'next-intl';
|
|
-import type { MessageKey } from '@/server/utils/errors';
|
|
|
|
|
|
+import { useAction } from 'next-safe-action/hook';
|
|
|
|
+import { getTotpUriAction } from '@/actions/settings/get-totp-uri';
|
|
|
|
+import { setupTotpAction } from '@/actions/settings/setup-totp-action';
|
|
|
|
+import { disableTotpAction } from '@/actions/settings/disable-totp';
|
|
|
|
|
|
-export const OtpForm = () => {
|
|
|
|
- const globalT = useTranslations();
|
|
|
|
|
|
+export const OtpForm = (props: { totpEnabled: boolean }) => {
|
|
|
|
+ const { totpEnabled } = props;
|
|
const t = useTranslations('settings.security');
|
|
const t = useTranslations('settings.security');
|
|
const [password, setPassword] = React.useState('');
|
|
const [password, setPassword] = React.useState('');
|
|
const [key, setKey] = React.useState('');
|
|
const [key, setKey] = React.useState('');
|
|
@@ -23,54 +25,53 @@ export const OtpForm = () => {
|
|
const setupOtpDisclosure = useDisclosure();
|
|
const setupOtpDisclosure = useDisclosure();
|
|
const disableOtpDisclosure = useDisclosure();
|
|
const disableOtpDisclosure = useDisclosure();
|
|
|
|
|
|
- const ctx = trpc.useContext();
|
|
|
|
- const me = trpc.auth.me.useQuery();
|
|
|
|
-
|
|
|
|
- const getTotpUri = trpc.auth.getTotpUri.useMutation({
|
|
|
|
- onMutate: () => {
|
|
|
|
|
|
+ const getTotpUriMutation = useAction(getTotpUriAction, {
|
|
|
|
+ onExecute: () => {
|
|
setupOtpDisclosure.close();
|
|
setupOtpDisclosure.close();
|
|
},
|
|
},
|
|
- onError: (e) => {
|
|
|
|
- setPassword('');
|
|
|
|
- toast.error(globalT(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables }));
|
|
|
|
- },
|
|
|
|
onSuccess: (data) => {
|
|
onSuccess: (data) => {
|
|
- setKey(data.key);
|
|
|
|
- setUri(data.uri);
|
|
|
|
|
|
+ if (!data.success) {
|
|
|
|
+ setPassword('');
|
|
|
|
+ toast.error(data.failure.reason);
|
|
|
|
+ } else {
|
|
|
|
+ setKey(data.key);
|
|
|
|
+ setUri(data.uri);
|
|
|
|
+ }
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
- const setupTotp = trpc.auth.setupTotp.useMutation({
|
|
|
|
- onMutate: () => {},
|
|
|
|
- onError: (e) => {
|
|
|
|
- setTotpCode('');
|
|
|
|
- toast.error(globalT(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables }));
|
|
|
|
- },
|
|
|
|
- onSuccess: () => {
|
|
|
|
- setTotpCode('');
|
|
|
|
- setKey('');
|
|
|
|
- setUri('');
|
|
|
|
- toast.success(t('2fa-enable-success'));
|
|
|
|
- ctx.auth.me.invalidate();
|
|
|
|
|
|
+ const setupTotpMutation = useAction(setupTotpAction, {
|
|
|
|
+ onSuccess: (data) => {
|
|
|
|
+ if (!data.success) {
|
|
|
|
+ setTotpCode('');
|
|
|
|
+ toast.error(data.failure.reason);
|
|
|
|
+ } else {
|
|
|
|
+ setTotpCode('');
|
|
|
|
+ setKey('');
|
|
|
|
+ setUri('');
|
|
|
|
+ toast.success(t('2fa-enable-success'));
|
|
|
|
+ // ctx.auth.me.invalidate();
|
|
|
|
+ }
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
- const disableTotp = trpc.auth.disableTotp.useMutation({
|
|
|
|
- onMutate: () => {
|
|
|
|
|
|
+ const disableTotpMutation = useAction(disableTotpAction, {
|
|
|
|
+ onExecute: () => {
|
|
disableOtpDisclosure.close();
|
|
disableOtpDisclosure.close();
|
|
},
|
|
},
|
|
- onError: (e) => {
|
|
|
|
- setPassword('');
|
|
|
|
- toast.error(globalT(e.data?.tError.message as MessageKey, { ...e.data?.tError?.variables }));
|
|
|
|
- },
|
|
|
|
- onSuccess: () => {
|
|
|
|
- toast.success(t('2fa-disable-success'));
|
|
|
|
- ctx.auth.me.invalidate();
|
|
|
|
|
|
+ onSuccess: (data) => {
|
|
|
|
+ if (!data.success) {
|
|
|
|
+ setPassword('');
|
|
|
|
+ toast.error(data.failure.reason);
|
|
|
|
+ } else {
|
|
|
|
+ toast.success(t('2fa-disable-success'));
|
|
|
|
+ //ctx.auth.me.invalidate();
|
|
|
|
+ }
|
|
},
|
|
},
|
|
});
|
|
});
|
|
|
|
|
|
const renderSetupQr = () => {
|
|
const renderSetupQr = () => {
|
|
- if (!uri || me.data?.totpEnabled) return null;
|
|
|
|
|
|
+ if (!uri || totpEnabled) return null;
|
|
|
|
|
|
return (
|
|
return (
|
|
<div className="mt-4">
|
|
<div className="mt-4">
|
|
@@ -85,7 +86,7 @@ export const OtpForm = () => {
|
|
<div className="mb-4">
|
|
<div className="mb-4">
|
|
<p className="text-muted">{t('enter-2fa-code')}</p>
|
|
<p className="text-muted">{t('enter-2fa-code')}</p>
|
|
<OtpInput value={totpCode} valueLength={6} onChange={(e) => setTotpCode(e)} />
|
|
<OtpInput value={totpCode} valueLength={6} onChange={(e) => setTotpCode(e)} />
|
|
- <Button disabled={totpCode.trim().length < 6} onClick={() => setupTotp.mutate({ totpCode })} className="mt-3 btn-success">
|
|
|
|
|
|
+ <Button disabled={totpCode.trim().length < 6} onClick={() => setupTotpMutation.execute({ totpCode })} className="mt-3 btn-success">
|
|
{t('enable-2fa')}
|
|
{t('enable-2fa')}
|
|
</Button>
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
@@ -103,8 +104,8 @@ export const OtpForm = () => {
|
|
|
|
|
|
return (
|
|
return (
|
|
<>
|
|
<>
|
|
- {!key && <Switch disabled={!me.isSuccess} onCheckedChange={handleTotp} checked={me.data?.totpEnabled} label={t('enable-2fa')} />}
|
|
|
|
- {getTotpUri.isLoading && (
|
|
|
|
|
|
+ {!key && <Switch onCheckedChange={handleTotp} checked={totpEnabled} label={t('enable-2fa')} />}
|
|
|
|
+ {getTotpUriMutation.isExecuting && (
|
|
<div className="progress w-50">
|
|
<div className="progress w-50">
|
|
<div className="progress-bar progress-bar-indeterminate bg-green" />
|
|
<div className="progress-bar progress-bar-indeterminate bg-green" />
|
|
</div>
|
|
</div>
|
|
@@ -119,12 +120,12 @@ export const OtpForm = () => {
|
|
<form
|
|
<form
|
|
onSubmit={(e) => {
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
- getTotpUri.mutate({ password });
|
|
|
|
|
|
+ getTotpUriMutation.execute({ password });
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
<p className="text-muted">{t('password-needed-hint')}</p>
|
|
<p className="text-muted">{t('password-needed-hint')}</p>
|
|
<Input name="password" type="password" onChange={(e) => setPassword(e.target.value)} placeholder={t('form.password')} />
|
|
<Input name="password" type="password" onChange={(e) => setPassword(e.target.value)} placeholder={t('form.password')} />
|
|
- <Button loading={getTotpUri.isLoading} type="submit" className="btn-success mt-3">
|
|
|
|
|
|
+ <Button loading={getTotpUriMutation.isExecuting} type="submit" className="btn-success mt-3">
|
|
{t('enable-2fa')}
|
|
{t('enable-2fa')}
|
|
</Button>
|
|
</Button>
|
|
</form>
|
|
</form>
|
|
@@ -140,12 +141,12 @@ export const OtpForm = () => {
|
|
<form
|
|
<form
|
|
onSubmit={(e) => {
|
|
onSubmit={(e) => {
|
|
e.preventDefault();
|
|
e.preventDefault();
|
|
- disableTotp.mutate({ password });
|
|
|
|
|
|
+ disableTotpMutation.execute({ password });
|
|
}}
|
|
}}
|
|
>
|
|
>
|
|
<p className="text-muted">{t('password-needed-hint')}</p>
|
|
<p className="text-muted">{t('password-needed-hint')}</p>
|
|
<Input name="password" type="password" onChange={(e) => setPassword(e.target.value)} placeholder={t('form.password')} />
|
|
<Input name="password" type="password" onChange={(e) => setPassword(e.target.value)} placeholder={t('form.password')} />
|
|
- <Button loading={disableTotp.isLoading} type="submit" className="btn-danger mt-3">
|
|
|
|
|
|
+ <Button loading={disableTotpMutation.isExecuting} type="submit" className="btn-danger mt-3">
|
|
{t('disable-2fa')}
|
|
{t('disable-2fa')}
|
|
</Button>
|
|
</Button>
|
|
</form>
|
|
</form>
|