'use client'; import {zodResolver} from '@hookform/resolvers/zod'; import {useMutation, useQuery} from '@tanstack/react-query'; import Link from 'next/link'; import {useRouter, useSearchParams} from 'next/navigation'; import {useState} from 'react'; import {useForm} from 'react-hook-form'; import {z} from 'zod'; import {InlineMessage} from '@/components/feedback/inline-message'; import {useAuth} from '@/components/providers/auth-provider'; import {AppCard} from '@/components/ui/app-card'; import {getOAuthAuthorizeUrl, login} from '@/features/auth/api/auth'; import {getPublicStatus} from '@/features/auth/api/public'; import {AuthButton, AuthFormField, AuthInput, SecondaryButton,} from '@/features/auth/components/auth-form-primitives'; import {PublicAuthGuard} from '@/features/auth/components/public-auth-guard'; const TEXT = { usernameRequired: '\u8bf7\u8f93\u5165\u7528\u6237\u540d', passwordRequired: '\u8bf7\u8f93\u5165\u5bc6\u7801', loginFailed: '\u767b\u5f55\u5931\u8d25\uff0c\u8bf7\u7a0d\u540e\u91cd\u8bd5\u3002', oauthUnavailable: '第三方登录当前不可用。', title: '\u7528\u6237\u767b\u5f55', username: '\u7528\u6237\u540d', password: '\u5bc6\u7801', loginPending: '\u767b\u5f55\u4e2d...', login: '\u767b\u5f55', forgotPassword: '\u5fd8\u8bb0\u5bc6\u7801\uff1f', register: '\u6ce8\u518c', }; const loginSchema = z.object({ username: z.string().min(1, TEXT.usernameRequired), password: z.string().min(1, TEXT.passwordRequired), }); type LoginFormValues = z.infer; export function LoginForm() { const router = useRouter(); const searchParams = useSearchParams(); const { setUser } = useAuth(); const [errorMessage, setErrorMessage] = useState(''); const redirect = searchParams?.get('redirect') || '/'; const form = useForm({ resolver: zodResolver(loginSchema), defaultValues: { username: '', password: '', }, }); const statusQuery = useQuery({ queryKey: ['public-status'], queryFn: getPublicStatus, }); const loginMutation = useMutation({ mutationFn: login, onSuccess: (user) => { setUser(user); router.replace(redirect); }, onError: (error: Error) => { setErrorMessage(error.message || TEXT.loginFailed); }, }); const oauthMutation = useMutation({ mutationFn: getOAuthAuthorizeUrl, onSuccess: (result) => { window.location.href = result.authorize_url; }, onError: (error: Error) => { setErrorMessage(error.message || TEXT.oauthUnavailable); }, }); const handleSubmit = form.handleSubmit((values) => { setErrorMessage(''); loginMutation.mutate(values); }); const handleOAuthLogin = (sourceName: string) => { setErrorMessage(''); oauthMutation.mutate(sourceName); }; return (
{form.formState.errors.username ? ( {form.formState.errors.username.message} ) : null} {form.formState.errors.password ? ( {form.formState.errors.password.message} ) : null} {errorMessage ? ( ) : null}
{loginMutation.isPending ? TEXT.loginPending : TEXT.login}
{(statusQuery.data?.auth_sources ?? []).length > 0 ? (
第三方账号登录
{(statusQuery.data?.auth_sources ?? []).map((source) => ( handleOAuthLogin(source.name)} className="min-w-36" disabled={oauthMutation.isPending} > {source.display_name || source.name} 登录 ))}
) : null}
{TEXT.forgotPassword}
); }