Compare commits

..

3 Commits

Author SHA1 Message Date
semantic-release-bot bbd44f0107 🔖 chore(release): v1.0.0-beta.20 [skip ci]
# [1.0.0-beta.20](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2025-02-23)

### 🐛 Bug Fixes

* **auth**: Add error handling to form submission and reset Turnstile on failure ([715d011](https://github.com/perfect-panel/ppanel-web/commit/715d011))
* **subscribe**: Update subscription domain placeholder to include examples; improve site name retrieval in global store ([c65a44c](https://github.com/perfect-panel/ppanel-web/commit/c65a44c))
2025-02-23 06:31:01 +00:00
web@ppanel c65a44c2c7 🐛 fix(subscribe): Update subscription domain placeholder to include examples; improve site name retrieval in global store 2025-02-23 13:25:01 +07:00
web@ppanel 715d0113ca 🐛 fix(auth): Add error handling to form submission and reset Turnstile on failure 2025-02-23 13:12:25 +07:00
14 changed files with 119 additions and 46 deletions
+8
View File
@@ -1,6 +1,14 @@
<a name="readme-top"></a> <a name="readme-top"></a>
# Changelog # Changelog
# [1.0.0-beta.20](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.19...v1.0.0-beta.20) (2025-02-23)
### 🐛 Bug Fixes
* **auth**: Add error handling to form submission and reset Turnstile on failure ([715d011](https://github.com/perfect-panel/ppanel-web/commit/715d011))
* **subscribe**: Update subscription domain placeholder to include examples; improve site name retrieval in global store ([c65a44c](https://github.com/perfect-panel/ppanel-web/commit/c65a44c))
# [1.0.0-beta.19](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2025-02-23) # [1.0.0-beta.19](https://github.com/perfect-panel/ppanel-web/compare/v1.0.0-beta.18...v1.0.0-beta.19) (2025-02-23)
+13 -4
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>
+24 -10
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>
+15 -5
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>
+26 -12
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;
@@ -92,7 +92,7 @@ export default function SubscribeConfig() {
<TableCell className='text-right'> <TableCell className='text-right'>
<Textarea <Textarea
className='h-52' className='h-52'
placeholder={t('subscriptionDomainPlaceholder')} placeholder={`${t('subscriptionDomainPlaceholder')}\nexample.com\nwww.example.com`}
defaultValue={data?.subscribe_domain} defaultValue={data?.subscribe_domain}
onBlur={(e) => { onBlur={(e) => {
updateConfig('subscribe_domain', e.target.value); updateConfig('subscribe_domain', e.target.value);
+5 -2
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 (
+5 -2
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 (
+5 -2
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 (
+5 -2
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 (
+5 -2
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 (
+5 -2
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 (
+1 -1
View File
@@ -104,7 +104,7 @@ export const useGlobalStore = create<GlobalStore>((set, get) => ({
}); });
}, },
getAppSubLink: (type: string, url: string) => { getAppSubLink: (type: string, url: string) => {
const name = get().common.site.site_name || ''; const name = get().common?.site?.site_name || '';
switch (type) { switch (type) {
case 'Clash': case 'Clash':
return `clash://install-config?url=${url}&name=${name}`; return `clash://install-config?url=${url}&name=${name}`;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "ppanel-web", "name": "ppanel-web",
"version": "1.0.0-beta.19", "version": "1.0.0-beta.20",
"private": true, "private": true,
"homepage": "https://github.com/perfect-panel/ppanel-web", "homepage": "https://github.com/perfect-panel/ppanel-web",
"bugs": { "bugs": {