new change

This commit is contained in:
chenZB666
2025-10-09 23:40:40 +08:00
parent 0645948fcc
commit e1b73b82a2
5 changed files with 352 additions and 30 deletions

View File

@@ -0,0 +1,50 @@
// 登录API端点
import { login } from '@/lib/api/actions';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
try {
// 验证请求方法
if (request.method !== 'POST') {
return NextResponse.json({ success: false, error: '不支持的请求方法' }, { status: 405 });
}
// 验证请求体
let credentials;
try {
credentials = await request.json();
} catch (jsonError) {
return NextResponse.json({ success: false, error: '请求体格式错误' }, { status: 400 });
}
// 基本参数验证
if (!credentials || typeof credentials !== 'object') {
return NextResponse.json({ success: false, error: '请求参数无效' }, { status: 400 });
}
// 调用登录函数
const result = await login(credentials);
// 根据结果返回响应
if (result.success) {
// 设置CORS头
const response = NextResponse.json({ success: true, user: result.user }, { status: 200 });
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'POST');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
return response;
} else {
// 根据不同错误类型返回不同的状态码
const statusCode = result.error.includes('不存在') || result.error.includes('错误') ? 401 : 400;
return NextResponse.json({ success: false, error: result.error }, { status: statusCode });
}
} catch (error) {
console.error('Login API error:', error);
// 对于不同类型的错误返回具体错误信息
if (error instanceof Error) {
return NextResponse.json({ success: false, error: error.message || '服务器内部错误' }, { status: 500 });
}
return NextResponse.json({ success: false, error: '服务器内部错误' }, { status: 500 });
}
}

View File

@@ -0,0 +1,69 @@
// src/app/api/auth/logout/route.ts
import { NextResponse } from 'next/server';
import { cookies } from 'next/headers';
// 退出登录API端点
export async function POST() {
try {
// 获取cookie存储
const cookieStore = cookies();
// 清除所有与next-auth相关的cookie
const allCookies = cookieStore.getAll();
allCookies.forEach(cookie => {
if (cookie.name.includes('next-auth')) {
cookieStore.delete(cookie.name);
}
});
// 返回成功响应
return NextResponse.json({
success: true,
message: '退出登录成功'
}, { status: 200 });
} catch (error) {
console.error('退出登录API错误:', error);
// 对于不同类型的错误返回具体错误信息
if (error instanceof Error) {
return NextResponse.json({
success: false,
error: error.message || '服务器内部错误'
}, { status: 500 });
}
return NextResponse.json({
success: false,
error: '服务器内部错误'
}, { status: 500 });
}
}
// 处理GET请求重定向到登录页面
export async function GET() {
try {
// 创建重定向响应到登录页面
const response = NextResponse.redirect(new URL('/login', process.env.NEXTAUTH_URL || 'http://localhost:3000'));
// 清除所有与next-auth相关的cookie
const cookieStore = cookies();
const allCookies = cookieStore.getAll();
allCookies.forEach(cookie => {
if (cookie.name.includes('next-auth')) {
cookieStore.delete(cookie.name);
}
});
return response;
} catch (error) {
console.error('退出登录重定向错误:', error);
// 发生错误时,仍然尝试重定向到登录页面
try {
return NextResponse.redirect(new URL('/login', process.env.NEXTAUTH_URL || 'http://localhost:3000'));
} catch (redirectError) {
// 如果重定向也失败,返回错误信息
return NextResponse.json({
success: false,
error: '退出登录失败,请刷新页面重试'
}, { status: 500 });
}
}
}

View File

@@ -0,0 +1,67 @@
// 注册API端点
import { register } from '@/lib/api/actions';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(request: NextRequest) {
try {
// 验证请求方法
if (request.method !== 'POST') {
return NextResponse.json({ success: false, error: '不支持的请求方法' }, { status: 405 });
}
// 验证请求体
let userData;
try {
userData = await request.json();
} catch (jsonError) {
return NextResponse.json({ success: false, error: '请求体格式错误' }, { status: 400 });
}
// 基本参数验证
if (!userData || typeof userData !== 'object') {
return NextResponse.json({ success: false, error: '请求参数无效' }, { status: 400 });
}
// 调用注册函数
const result = await register(userData);
// 根据结果返回响应
if (result.success) {
// 设置CORS头
const response = NextResponse.json({
success: true,
message: '注册成功',
user: result.user
}, { status: 201 });
response.headers.set('Access-Control-Allow-Origin', '*');
response.headers.set('Access-Control-Allow-Methods', 'POST');
response.headers.set('Access-Control-Allow-Headers', 'Content-Type');
return response;
} else {
// 根据不同错误类型返回不同的状态码
let statusCode = 400;
if (result.error.includes('已被注册')) {
statusCode = 409; // 冲突状态码
}
return NextResponse.json({
success: false,
error: result.error || '注册失败,请检查您的信息'
}, { status: statusCode });
}
} catch (error) {
console.error('注册API错误:', error);
// 对于不同类型的错误返回具体错误信息
if (error instanceof Error) {
return NextResponse.json({
success: false,
error: error.message || '服务器内部错误'
}, { status: 500 });
}
return NextResponse.json({
success: false,
error: '服务器内部错误'
}, { status: 500 });
}
}

View File

@@ -6,6 +6,9 @@ import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { signIn } from 'next-auth/react';
import { register } from '@/lib/api/actions';
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert';
import { Eye, EyeOff } from 'lucide-react';
interface AuthFormProps {
type: 'login' | 'register';
@@ -17,6 +20,9 @@ export default function AuthForm({ type }: AuthFormProps) {
const [password, setPassword] = useState('');
const [email, setEmail] = useState('');
const [minecraftUsername, setMinecraftUsername] = useState('');
const [error, setError] = useState<string | null>(null);
const [success, setSuccess] = useState<string | null>(null);
const [showPassword, setShowPassword] = useState(false);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
@@ -24,39 +30,108 @@ export default function AuthForm({ type }: AuthFormProps) {
try {
if (type === 'login') {
// 使用next-auth的signIn功能进行登录
// 基本验证
if (!password) {
setError('请输入密码');
setIsLoading(false);
return;
}
const usernameField = username || email;
if (!usernameField) {
setError('请输入用户名或邮箱');
setIsLoading(false);
return;
}
// 使用next-auth的signIn功能进行登录设置redirect为false以便处理错误
const result = await signIn('credentials', {
username: username || email, // 使用username或email作为用户名字段
username: usernameField, // 使用username或email作为用户名字段
email: email, // 传递email字段
password,
redirect: true, // 登录成功后自动重定向
redirect: false, // 不自动重定向,以便处理错误
callbackUrl: '/user-home' // 指定重定向目标
});
console.log('登录结果:', result);
if (result?.error) {
// 处理登录错误 - 根据错误类型显示不同的错误信息
console.error('登录失败:', result.error);
if (result.error.includes('CredentialsSignin')) {
setError('用户名或密码错误,请重试');
} else if (result.error.includes('Network')) {
setError('网络连接失败,请检查您的网络设置');
} else {
setError('登录失败,请稍后再试');
}
} else if (result?.ok) {
// 登录成功,手动重定向
window.location.href = '/user-home';
} else {
setError('登录过程中发生未知错误');
}
} else {
// 注册逻辑可以在这里实现
console.log('注册信息', { username, password, email, minecraftUsername });
// 模拟注册请求
await new Promise(resolve => setTimeout(resolve, 1000));
// 调用注册API
const result = await register({
username,
password,
email,
minecraftUsername
});
if (result.success) {
setSuccess('注册成功!即将跳转到登录页面...');
// 注册成功后重定向到登录页面
setTimeout(() => {
window.location.href = '/login';
}, 2000);
} else {
setError(result.error || '注册失败,请稍后再试');
}
}
} catch (error) {
console.error('认证失败:', error);
// 这里可以添加错误处理逻辑
setError('网络错误,请检查您的连接');
} finally {
setIsLoading(false);
}
};
// 重置错误和成功消息
const resetMessages = () => {
setError(null);
setSuccess(null);
};
// 监听输入变化,清除错误消息
const handleInputChange = (setter: React.Dispatch<React.SetStateAction<string>>) => {
return (e: React.ChangeEvent<HTMLInputElement>) => {
resetMessages();
setter(e.target.value);
};
};
return (
<form onSubmit={handleSubmit} className="space-y-4">
{error && (
<Alert variant="destructive" className="bg-red-50 border-red-200 text-red-800">
<AlertTitle></AlertTitle>
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{success && (
<Alert className="bg-green-50 border-green-200 text-green-800">
<AlertTitle></AlertTitle>
<AlertDescription>{success}</AlertDescription>
</Alert>
)}
<div>
<Label htmlFor="username"></Label>
<Input
id="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
onChange={handleInputChange(setUsername)}
placeholder={type === 'login' ? "用户名或邮箱" : "输入用户名"}
/>
</div>
@@ -68,20 +143,29 @@ export default function AuthForm({ type }: AuthFormProps) {
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
onChange={handleInputChange(setEmail)}
placeholder="或直接输入邮箱登录"
/>
</div>
)}
<div>
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
required
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<div className="relative">
<Input
id="password"
type={showPassword ? "text" : "password"}
required
value={password}
onChange={handleInputChange(setPassword)}
/>
<button
type="button"
className="absolute right-3 top-1/2 transform -translate-y-1/2 text-gray-500 hover:text-gray-700 focus:outline-none"
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
</div>
{type === 'register' && (
@@ -89,22 +173,23 @@ export default function AuthForm({ type }: AuthFormProps) {
<div>
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
required
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
id="email"
type="email"
required
value={email}
onChange={handleInputChange(setEmail)}
/>
</div>
<div>
<Label htmlFor="minecraftUsername">Minecraft </Label>
<Input
id="minecraftUsername"
type="text"
required
value={minecraftUsername}
onChange={(e) => setMinecraftUsername(e.target.value)}
/>
id="minecraftUsername"
type="text"
required
value={minecraftUsername}
onChange={handleInputChange(setMinecraftUsername)}
placeholder="您的Minecraft游戏内用户名"
/>
</div>
</>
)}

View File

@@ -0,0 +1,51 @@
// src/components/ui/alert.tsx
import React from 'react';
interface AlertProps {
children: React.ReactNode;
variant?: 'default' | 'destructive';
className?: string;
}
interface AlertTitleProps {
children: React.ReactNode;
className?: string;
}
interface AlertDescriptionProps {
children: React.ReactNode;
className?: string;
}
// 主Alert组件
export function Alert({ children, variant = 'default', className = '' }: AlertProps) {
const baseClasses = 'p-4 rounded-lg border flex items-start space-x-3';
const variantClasses = {
default: 'bg-green-50 border-green-200 text-green-800',
destructive: 'bg-red-50 border-red-200 text-red-800'
};
return (
<div className={`${baseClasses} ${variantClasses[variant]} ${className}`}>
{children}
</div>
);
}
// Alert标题组件
export function AlertTitle({ children, className = '' }: AlertTitleProps) {
return (
<h3 className={`font-medium text-sm ${className}`}>
{children}
</h3>
);
}
// Alert描述组件
export function AlertDescription({ children, className = '' }: AlertDescriptionProps) {
return (
<p className={`text-sm ${className}`}>
{children}
</p>
);
}