add email captcha
This commit is contained in:
parent
f45fcc3321
commit
fe4fbf0ea1
@ -11,11 +11,13 @@ import { Separator } from "@/components/ui/separator"
|
||||
import { Mail, Chrome } from "lucide-react"
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert"
|
||||
import { ThemeToggle } from "@/components/theme-toggle"
|
||||
import { CaptchaDialog } from "@/components/captcha-dialog"
|
||||
|
||||
function SignInContent() {
|
||||
const [email, setEmail] = useState("")
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [message, setMessage] = useState("")
|
||||
const [showCaptcha, setShowCaptcha] = useState(false)
|
||||
const { data: session, status } = useSession()
|
||||
const router = useRouter()
|
||||
const searchParams = useSearchParams()
|
||||
@ -54,8 +56,13 @@ function SignInContent() {
|
||||
return null
|
||||
}
|
||||
|
||||
const handleEmailSignIn = async (e: React.FormEvent) => {
|
||||
const handleEmailSignInClick = (e: React.FormEvent) => {
|
||||
e.preventDefault()
|
||||
if (!email) return
|
||||
setShowCaptcha(true)
|
||||
}
|
||||
|
||||
const handleCaptchaVerify = async () => {
|
||||
setIsLoading(true)
|
||||
setMessage("")
|
||||
|
||||
@ -77,6 +84,10 @@ function SignInContent() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleCaptchaCancel = () => {
|
||||
setMessage("")
|
||||
}
|
||||
|
||||
const handleGoogleSignIn = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
@ -123,7 +134,7 @@ function SignInContent() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleEmailSignIn} className="space-y-4">
|
||||
<form onSubmit={handleEmailSignInClick} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email" className="text-foreground">邮箱地址</Label>
|
||||
<Input
|
||||
@ -154,6 +165,14 @@ function SignInContent() {
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* 验证码弹窗 */}
|
||||
<CaptchaDialog
|
||||
open={showCaptcha}
|
||||
onOpenChange={setShowCaptcha}
|
||||
onVerify={handleCaptchaVerify}
|
||||
onCancel={handleCaptchaCancel}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
163
components/captcha-dialog.tsx
Normal file
163
components/captcha-dialog.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
"use client"
|
||||
|
||||
import { useState, useEffect } from "react"
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Input } from "@/components/ui/input"
|
||||
import { Label } from "@/components/ui/label"
|
||||
import { Alert, AlertDescription } from "@/components/ui/alert"
|
||||
import { Shield, RefreshCw, AlertCircle } from "lucide-react"
|
||||
|
||||
interface CaptchaDialogProps {
|
||||
open: boolean
|
||||
onOpenChange: (open: boolean) => void
|
||||
onVerify: () => void
|
||||
onCancel: () => void
|
||||
}
|
||||
|
||||
export function CaptchaDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onVerify,
|
||||
onCancel
|
||||
}: CaptchaDialogProps) {
|
||||
const [num1, setNum1] = useState(0)
|
||||
const [num2, setNum2] = useState(0)
|
||||
const [userAnswer, setUserAnswer] = useState("")
|
||||
const [error, setError] = useState("")
|
||||
|
||||
const generateCaptcha = () => {
|
||||
const newNum1 = Math.floor(Math.random() * 10) + 1
|
||||
const newNum2 = Math.floor(Math.random() * 10) + 1
|
||||
setNum1(newNum1)
|
||||
setNum2(newNum2)
|
||||
setUserAnswer("")
|
||||
setError("")
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
generateCaptcha()
|
||||
}
|
||||
}, [open])
|
||||
|
||||
const handleVerify = () => {
|
||||
const correctAnswer = num1 + num2
|
||||
const inputAnswer = parseInt(userAnswer)
|
||||
|
||||
if (isNaN(inputAnswer) || inputAnswer !== correctAnswer) {
|
||||
setError("验证码错误,请重新计算")
|
||||
generateCaptcha()
|
||||
return
|
||||
}
|
||||
|
||||
setError("")
|
||||
onVerify()
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
const handleCancel = () => {
|
||||
setUserAnswer("")
|
||||
setError("")
|
||||
onCancel()
|
||||
onOpenChange(false)
|
||||
}
|
||||
|
||||
const handleKeyPress = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleVerify()
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2 text-foreground">
|
||||
<Shield className="h-5 w-5 text-blue-500 dark:text-blue-400" />
|
||||
安全验证
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
为了防止恶意使用,请完成以下验证码验证后再发送登录邮件
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4">
|
||||
<Alert className="border-blue-200 bg-blue-50 dark:border-blue-800 dark:bg-blue-950/50">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>
|
||||
<strong>提示:</strong>计算下面的数学题,输入答案完成验证
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-center p-6 bg-muted/50 rounded-lg border">
|
||||
<div className="text-center">
|
||||
<div className="text-2xl font-bold text-foreground mb-2">
|
||||
{num1} + {num2} = ?
|
||||
</div>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={generateCaptcha}
|
||||
className="text-muted-foreground hover:text-foreground"
|
||||
>
|
||||
<RefreshCw className="h-4 w-4 mr-1" />
|
||||
换一题
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="captcha-answer" className="text-foreground">
|
||||
请输入答案
|
||||
</Label>
|
||||
<Input
|
||||
id="captcha-answer"
|
||||
type="number"
|
||||
placeholder="输入计算结果"
|
||||
value={userAnswer}
|
||||
onChange={(e) => setUserAnswer(e.target.value)}
|
||||
onKeyPress={handleKeyPress}
|
||||
className="text-center text-lg font-medium"
|
||||
autoFocus
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<Alert className="border-red-200 bg-red-50 text-red-800 dark:border-red-800 dark:bg-red-950 dark:text-red-200">
|
||||
<AlertCircle className="h-4 w-4" />
|
||||
<AlertDescription>{error}</AlertDescription>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter className="flex justify-between">
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={handleCancel}
|
||||
>
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleVerify}
|
||||
disabled={!userAnswer.trim()}
|
||||
className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-600 dark:hover:bg-blue-700"
|
||||
>
|
||||
验证并发送邮件
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
Loading…
Reference in New Issue
Block a user