🐛 fix(auth): Add error handling to form submission and reset Turnstile on failure

This commit is contained in:
web@ppanel 2025-02-23 13:12:25 +07:00
parent 0f15fb82cb
commit 715d0113ca
10 changed files with 108 additions and 43 deletions

View File

@ -5,10 +5,10 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
import { Input } from '@workspace/ui/components/input'; import { Input } from '@workspace/ui/components/input';
import { Icon } from '@workspace/ui/custom-components/icon'; import { Icon } from '@workspace/ui/custom-components/icon';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction, useRef } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import CloudFlareTurnstile from '../turnstile'; import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
export default function LoginForm({ export default function LoginForm({
loading, loading,
@ -38,10 +38,19 @@ export default function LoginForm({
defaultValues: initialValues, defaultValues: initialValues,
}); });
const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => {
try {
onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
});
return ( return (
<> <>
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'> <form onSubmit={handleSubmit} className='grid gap-6'>
<FormField <FormField
control={form.control} control={form.control}
name='email' name='email'
@ -73,7 +82,7 @@ export default function LoginForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<CloudFlareTurnstile id='login' {...field} /> <CloudFlareTurnstile id='login' {...field} ref={turnstile} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -6,11 +6,11 @@ import { Input } from '@workspace/ui/components/input';
import { Icon } from '@workspace/ui/custom-components/icon'; import { Icon } from '@workspace/ui/custom-components/icon';
import { Markdown } from '@workspace/ui/custom-components/markdown'; import { Markdown } from '@workspace/ui/custom-components/markdown';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction, useRef } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import SendCode from '../send-code'; import SendCode from '../send-code';
import CloudFlareTurnstile from '../turnstile'; import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
export default function RegisterForm({ export default function RegisterForm({
loading, loading,
@ -31,11 +31,10 @@ export default function RegisterForm({
const handleCheckUser = async (email: string) => { const handleCheckUser = async (email: string) => {
try { try {
if (!auth.email.enable_verify) return true;
const domain = email.split('@')[1]; const domain = email.split('@')[1];
const isValid = const isValid = auth.email?.domain_suffix_list.split('\n').includes(domain || '');
!auth.email.enable_verify || return isValid;
auth.email?.domain_suffix_list.split('\n').includes(domain || '');
return !isValid;
} catch (error) { } catch (error) {
console.log('Error checking user:', error); console.log('Error checking user:', error);
return false; return false;
@ -53,7 +52,7 @@ export default function RegisterForm({
password: z.string(), password: z.string(),
repeat_password: z.string(), repeat_password: z.string(),
code: auth.email.enable_verify ? z.string() : z.string().nullish(), code: auth.email.enable_verify ? z.string() : z.string().nullish(),
invite: invite.forced_invite ? z.string() : z.string().nullish(), invite: invite.forced_invite ? z.string().min(1) : z.string().nullish(),
cf_token: cf_token:
verify.enable_register_verify && verify.turnstile_site_key verify.enable_register_verify && verify.turnstile_site_key
? z.string() ? z.string()
@ -77,13 +76,22 @@ export default function RegisterForm({
}, },
}); });
const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => {
try {
onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
});
return ( return (
<> <>
{auth.register.stop_register ? ( {auth.register.stop_register ? (
<Markdown>{t('message')}</Markdown> <Markdown>{t('message')}</Markdown>
) : ( ) : (
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'> <form onSubmit={handleSubmit} className='grid gap-6'>
<FormField <FormField
control={form.control} control={form.control}
name='email' name='email'
@ -114,7 +122,12 @@ export default function RegisterForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<Input placeholder='Enter password again...' type='password' {...field} /> <Input
disabled={loading}
placeholder='Enter password again...'
type='password'
{...field}
/>
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@ -129,6 +142,7 @@ export default function RegisterForm({
<FormControl> <FormControl>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Input <Input
disabled={loading}
placeholder='Enter code...' placeholder='Enter code...'
type='text' type='text'
{...field} {...field}
@ -172,7 +186,7 @@ export default function RegisterForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<CloudFlareTurnstile id='register' {...field} /> <CloudFlareTurnstile id='register' {...field} ref={turnstile} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -5,11 +5,11 @@ import { Form, FormControl, FormField, FormItem, FormMessage } from '@workspace/
import { Input } from '@workspace/ui/components/input'; import { Input } from '@workspace/ui/components/input';
import { Icon } from '@workspace/ui/custom-components/icon'; import { Icon } from '@workspace/ui/custom-components/icon';
import { useTranslations } from 'next-intl'; import { useTranslations } from 'next-intl';
import { Dispatch, SetStateAction } from 'react'; import { Dispatch, SetStateAction, useRef } from 'react';
import { useForm } from 'react-hook-form'; import { useForm } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import SendCode from '../send-code'; import SendCode from '../send-code';
import CloudFlareTurnstile from '../turnstile'; import CloudFlareTurnstile, { TurnstileRef } from '../turnstile';
export default function ResetForm({ export default function ResetForm({
loading, loading,
@ -43,10 +43,19 @@ export default function ResetForm({
defaultValues: initialValues, defaultValues: initialValues,
}); });
const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => {
try {
onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
});
return ( return (
<> <>
<Form {...form}> <Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className='grid gap-6'> <form onSubmit={handleSubmit} className='grid gap-6'>
<FormField <FormField
control={form.control} control={form.control}
name='email' name='email'
@ -67,6 +76,7 @@ export default function ResetForm({
<FormControl> <FormControl>
<div className='flex items-center gap-2'> <div className='flex items-center gap-2'>
<Input <Input
disabled={loading}
placeholder='Enter code...' placeholder='Enter code...'
type='text' type='text'
{...field} {...field}
@ -91,7 +101,7 @@ export default function ResetForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<Input placeholder='Enter your New password...' type='password' {...field} /> <Input placeholder='Enter your new password...' type='password' {...field} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>
@ -104,7 +114,7 @@ export default function ResetForm({
render={({ field }) => ( render={({ field }) => (
<FormItem> <FormItem>
<FormControl> <FormControl>
<CloudFlareTurnstile id='reset' {...field} /> <CloudFlareTurnstile id='reset' {...field} ref={turnstile} />
</FormControl> </FormControl>
<FormMessage /> <FormMessage />
</FormItem> </FormItem>

View File

@ -1,26 +1,38 @@
'use client'; 'use client';
import useGlobalStore from '@/config/use-global';
import { useLocale } from 'next-intl'; import { useLocale } from 'next-intl';
import { useTheme } from 'next-themes'; import { useTheme } from 'next-themes';
import { useEffect } from 'react'; import { forwardRef, useEffect, useImperativeHandle } from 'react';
import Turnstile, { useTurnstile } from 'react-turnstile'; import Turnstile, { useTurnstile } from 'react-turnstile';
export default function CloudFlareTurnstile({ import useGlobalStore from '@/config/use-global';
id,
value, export type TurnstileRef = {
onChange, reset: () => void;
}: { };
id?: string;
value?: null | string; const CloudFlareTurnstile = forwardRef<
onChange: (value?: string) => void; TurnstileRef,
}) { {
id?: string;
value?: null | string;
onChange: (value?: string) => void;
}
>(function CloudFlareTurnstile({ id, value, onChange }, ref) {
const { common } = useGlobalStore(); const { common } = useGlobalStore();
const { verify } = common; const { verify } = common;
const { resolvedTheme } = useTheme(); const { resolvedTheme } = useTheme();
const locale = useLocale(); const locale = useLocale();
const turnstile = useTurnstile(); const turnstile = useTurnstile();
useImperativeHandle(
ref,
() => ({
reset: () => turnstile.reset(),
}),
[turnstile],
);
useEffect(() => { useEffect(() => {
if (value === '') { if (value === '') {
turnstile.reset(); turnstile.reset();
@ -51,4 +63,6 @@ export default function CloudFlareTurnstile({
/> />
) )
); );
} });
export default CloudFlareTurnstile;

View File

@ -40,8 +40,11 @@ export default function LoginForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (

View File

@ -78,8 +78,11 @@ export default function RegisterForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (

View File

@ -45,8 +45,11 @@ export default function ResetForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (

View File

@ -47,8 +47,11 @@ export default function LoginForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (

View File

@ -65,8 +65,11 @@ export default function RegisterForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (

View File

@ -48,8 +48,11 @@ export default function ResetForm({
const turnstile = useRef<TurnstileRef>(null); const turnstile = useRef<TurnstileRef>(null);
const handleSubmit = form.handleSubmit((data) => { const handleSubmit = form.handleSubmit((data) => {
onSubmit(data); try {
turnstile.current?.reset(); onSubmit(data);
} catch (error) {
turnstile.current?.reset();
}
}); });
return ( return (