add email captcha

This commit is contained in:
songtianlun 2025-06-14 21:30:36 +08:00
parent f45fcc3321
commit fe4fbf0ea1
2 changed files with 184 additions and 2 deletions

View File

@ -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>
)
}

View 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>
)
}