new change
This commit is contained in:
@@ -1,9 +1,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import AuthForm from '@/components/auth/AuthForm';
|
||||||
import { Input } from '@/components/ui/input';
|
|
||||||
import { Label } from '@/components/ui/label';
|
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
|
import { Button } from '@/components/ui/button';
|
||||||
import Link from 'next/link';
|
import Link from 'next/link';
|
||||||
import Image from 'next/image';
|
import Image from 'next/image';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
@@ -65,52 +64,8 @@ export default function RegisterPage() {
|
|||||||
<div className="w-16 h-1 bg-gradient-to-r from-emerald-500 to-teal-500 mx-auto mt-2 rounded-full"></div>
|
<div className="w-16 h-1 bg-gradient-to-r from-emerald-500 to-teal-500 mx-auto mt-2 rounded-full"></div>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-6 px-6 pb-8">
|
<CardContent className="space-y-6 px-6 pb-8">
|
||||||
<form action="/api/auth/signup" method="post">
|
{/* 使用统一的认证表单组件 */}
|
||||||
<input type="hidden" name="callbackUrl" value={callbackUrl} />
|
<AuthForm type="register" />
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="username" className="text-gray-700 dark:text-gray-300">用户名</Label>
|
|
||||||
<Input
|
|
||||||
id="username"
|
|
||||||
name="username"
|
|
||||||
type="text"
|
|
||||||
placeholder="请输入用户名"
|
|
||||||
required
|
|
||||||
className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 dark:focus:border-emerald-400 transition-all rounded-xl"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="email" className="text-gray-700 dark:text-gray-300">邮箱</Label>
|
|
||||||
<Input
|
|
||||||
id="email"
|
|
||||||
name="email"
|
|
||||||
type="email"
|
|
||||||
placeholder="请输入您的邮箱"
|
|
||||||
required
|
|
||||||
className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 dark:focus:border-emerald-400 transition-all rounded-xl"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<Label htmlFor="password" className="text-gray-700 dark:text-gray-300">密码</Label>
|
|
||||||
<Input
|
|
||||||
id="password"
|
|
||||||
name="password"
|
|
||||||
type="password"
|
|
||||||
placeholder="请输入密码"
|
|
||||||
required
|
|
||||||
className="border-emerald-200 dark:border-emerald-900/30 focus:border-emerald-500 dark:focus:border-emerald-400 transition-all rounded-xl"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
className="w-full mt-6 bg-emerald-600 hover:bg-emerald-700 text-white transition-all duration-300 transform hover:-translate-y-1 shadow-md hover:shadow-lg rounded-xl py-6"
|
|
||||||
>
|
|
||||||
注册
|
|
||||||
</Button>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{/* 分割线 */}
|
{/* 分割线 */}
|
||||||
<div className="relative my-6">
|
<div className="relative my-6">
|
||||||
|
|||||||
@@ -325,7 +325,7 @@ MOCK-RSA-KEY-FOR-DEMO-PURPOSES-ONLY
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<Label htmlFor="character-description" className="text-base font-medium">角色描述(可选)</Label>
|
<Label htmlFor="character-description" className="text-base font-medium">角色描述(可选)</Label>
|
||||||
<Input
|
<textarea
|
||||||
id="character-description"
|
id="character-description"
|
||||||
name="description"
|
name="description"
|
||||||
value={characterForm.description}
|
value={characterForm.description}
|
||||||
|
|||||||
@@ -7,6 +7,17 @@ import CharacterCenterClient from './CharacterCenterClient';
|
|||||||
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardFooter, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
|
// 角色类型定义
|
||||||
|
interface Character {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
skinId: string;
|
||||||
|
created: string;
|
||||||
|
level: number;
|
||||||
|
description?: string;
|
||||||
|
isActive: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
export default async function CharacterCenter() {
|
export default async function CharacterCenter() {
|
||||||
// 检查用户登录状态
|
// 检查用户登录状态
|
||||||
const session = await getServerSession(authOptions);
|
const session = await getServerSession(authOptions);
|
||||||
@@ -20,7 +31,7 @@ export default async function CharacterCenter() {
|
|||||||
const userName = session.user?.name || '玩家';
|
const userName = session.user?.name || '玩家';
|
||||||
|
|
||||||
// 模拟的角色数据
|
// 模拟的角色数据
|
||||||
const characters = [];
|
const characters: Character[] = [];
|
||||||
|
|
||||||
// 渲染客户端组件
|
// 渲染客户端组件
|
||||||
return <CharacterCenterClient userName={userName} characters={characters} />;
|
return <CharacterCenterClient userName={userName} characters={characters} />;
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import Link from 'next/link';
|
|||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
import { getSession } from 'next-auth/react';
|
import { getSession } from 'next-auth/react';
|
||||||
|
import { Session } from 'next-auth';
|
||||||
|
|
||||||
export default function UserHome() {
|
export default function UserHome() {
|
||||||
const [session, setSession] = useState(null);
|
const [session, setSession] = useState<Session | null>(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -2,7 +2,62 @@
|
|||||||
'use server';
|
'use server';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || '/api';
|
// 配置axios实例,统一处理API请求
|
||||||
|
const apiClient = axios.create({
|
||||||
|
baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',
|
||||||
|
timeout: 10000, // 设置10秒超时
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加请求拦截器处理认证
|
||||||
|
apiClient.interceptors.request.use(
|
||||||
|
(config) => {
|
||||||
|
// 可以在这里添加认证token
|
||||||
|
return config;
|
||||||
|
},
|
||||||
|
(error) => {
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 添加响应拦截器统一处理错误
|
||||||
|
apiClient.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
// 处理常见错误状态码
|
||||||
|
if (error.response) {
|
||||||
|
switch (error.response.status) {
|
||||||
|
case 401:
|
||||||
|
// 未授权,可能需要重新登录
|
||||||
|
console.error('认证失败,需要重新登录');
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
// 禁止访问
|
||||||
|
console.error('权限不足');
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
// 资源不存在
|
||||||
|
console.error('请求的资源不存在');
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
// 服务器错误
|
||||||
|
console.error('服务器内部错误');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`API请求错误: ${error.response.status}`);
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
// 没有收到响应
|
||||||
|
console.error('网络错误,无法连接到服务器');
|
||||||
|
} else {
|
||||||
|
// 请求配置错误
|
||||||
|
console.error('请求配置错误:', error.message);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 导出退出登录函数 - 供服务器端使用
|
// 导出退出登录函数 - 供服务器端使用
|
||||||
export const serverSignOut = async () => {
|
export const serverSignOut = async () => {
|
||||||
@@ -44,11 +99,24 @@ export const login = async (credentials: {
|
|||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
// 对于测试环境,可以直接验证测试账号
|
// 对于测试环境,可以直接验证测试账号
|
||||||
const TEST_USERNAME = 'test';
|
const TEST_USERNAME = process.env.TEST_USERNAME || 'test';
|
||||||
const TEST_PASSWORD = 'test';
|
const TEST_PASSWORD = process.env.TEST_PASSWORD || 'test';
|
||||||
// 对于测试环境,可以直接验证测试账号 - 支持通过username或email字段登录
|
|
||||||
|
// 验证是否为空
|
||||||
|
if (!credentials.password) {
|
||||||
|
return { success: false, error: '请输入密码' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取用户名或邮箱字段
|
||||||
const usernameField = credentials.username || credentials.email;
|
const usernameField = credentials.username || credentials.email;
|
||||||
if (usernameField === TEST_USERNAME && credentials.password === TEST_PASSWORD) {
|
if (!usernameField) {
|
||||||
|
return { success: false, error: '请输入用户名或邮箱' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 支持通过username或email字段登录测试账号
|
||||||
|
if (process.env.NODE_ENV !== 'production' &&
|
||||||
|
usernameField === TEST_USERNAME &&
|
||||||
|
credentials.password === TEST_PASSWORD) {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
user: {
|
user: {
|
||||||
@@ -61,11 +129,20 @@ export const login = async (credentials: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 实际环境中调用API
|
// 实际环境中调用API
|
||||||
const response = await axios.post(`${API_URL}/auth/login`, credentials);
|
const response = await apiClient.post('/auth/login', credentials);
|
||||||
return { success: true, ...response.data };
|
return { success: true, ...response.data };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('登录失败:', error);
|
console.error('登录失败:', error);
|
||||||
return { success: false, error: '登录失败,请检查用户名和密码' };
|
// 根据错误类型提供更具体的错误信息
|
||||||
|
if (error instanceof axios.AxiosError) {
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
return { success: false, error: '用户名或密码错误,请重试' };
|
||||||
|
}
|
||||||
|
if (error.request) {
|
||||||
|
return { success: false, error: '网络连接失败,请检查您的网络设置' };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return { success: false, error: '登录失败,请稍后再试' };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,11 +154,56 @@ export const register = async (userData: {
|
|||||||
minecraftUsername: string;
|
minecraftUsername: string;
|
||||||
}) => {
|
}) => {
|
||||||
try {
|
try {
|
||||||
|
// 基本验证
|
||||||
|
if (!userData.username?.trim()) {
|
||||||
|
return { success: false, error: '用户名不能为空' };
|
||||||
|
}
|
||||||
|
if (!userData.password?.trim()) {
|
||||||
|
return { success: false, error: '密码不能为空' };
|
||||||
|
}
|
||||||
|
if (!userData.email?.trim()) {
|
||||||
|
return { success: false, error: '邮箱不能为空' };
|
||||||
|
}
|
||||||
|
if (!userData.minecraftUsername?.trim()) {
|
||||||
|
return { success: false, error: 'Minecraft用户名不能为空' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 密码强度检查(简单示例)
|
||||||
|
if (userData.password.length < 6) {
|
||||||
|
return { success: false, error: '密码长度至少为6位' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// 邮箱格式检查
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
if (!emailRegex.test(userData.email)) {
|
||||||
|
return { success: false, error: '请输入有效的邮箱地址' };
|
||||||
|
}
|
||||||
|
|
||||||
// 实际环境中调用API
|
// 实际环境中调用API
|
||||||
const response = await axios.post(`${API_URL}/auth/register`, userData);
|
const response = await apiClient.post('/auth/register', userData);
|
||||||
return { success: true, ...response.data };
|
return { success: true, ...response.data };
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('注册失败:', error);
|
console.error('注册失败:', error);
|
||||||
|
// 根据错误类型提供更具体的错误信息
|
||||||
|
if (error instanceof axios.AxiosError) {
|
||||||
|
if (error.response?.status === 400) {
|
||||||
|
// 从服务器获取具体错误信息
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: error.response.data?.error || '注册信息有误,请检查后重试'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (error.response?.status === 409) {
|
||||||
|
// 冲突,可能是用户名或邮箱已存在
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
error: '用户名或邮箱已被注册'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (error.request) {
|
||||||
|
return { success: false, error: '网络连接失败,请检查您的网络设置' };
|
||||||
|
}
|
||||||
|
}
|
||||||
return { success: false, error: '注册失败,请稍后再试' };
|
return { success: false, error: '注册失败,请稍后再试' };
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -3,7 +3,44 @@ import NextAuth, { AuthOptions } from 'next-auth';
|
|||||||
import CredentialsProvider from 'next-auth/providers/credentials';
|
import CredentialsProvider from 'next-auth/providers/credentials';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
const API_URL = process.env.NEXT_PUBLIC_API_URL || '/api';
|
// 配置axios实例,与actions.ts保持一致
|
||||||
|
const apiClient = axios.create({
|
||||||
|
baseURL: process.env.NEXT_PUBLIC_API_URL || '/api',
|
||||||
|
timeout: 10000,
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加响应拦截器,与actions.ts保持一致
|
||||||
|
apiClient.interceptors.response.use(
|
||||||
|
(response) => response,
|
||||||
|
(error) => {
|
||||||
|
if (error.response) {
|
||||||
|
switch (error.response.status) {
|
||||||
|
case 401:
|
||||||
|
console.error('认证失败,需要重新登录');
|
||||||
|
break;
|
||||||
|
case 403:
|
||||||
|
console.error('权限不足');
|
||||||
|
break;
|
||||||
|
case 404:
|
||||||
|
console.error('请求的资源不存在');
|
||||||
|
break;
|
||||||
|
case 500:
|
||||||
|
console.error('服务器内部错误');
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
console.error(`API请求错误: ${error.response.status}`);
|
||||||
|
}
|
||||||
|
} else if (error.request) {
|
||||||
|
console.error('网络错误,无法连接到服务器');
|
||||||
|
} else {
|
||||||
|
console.error('请求配置错误:', error.message);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
declare module "next-auth" {
|
declare module "next-auth" {
|
||||||
interface Session {
|
interface Session {
|
||||||
@@ -26,14 +63,16 @@ export const authOptions: AuthOptions = {
|
|||||||
password: { label: "密码", type: "password" }
|
password: { label: "密码", type: "password" }
|
||||||
},
|
},
|
||||||
async authorize(credentials) {
|
async authorize(credentials) {
|
||||||
// 默认测试账号 - 用于开发和测试环境
|
// 测试账号配置 - 从环境变量获取
|
||||||
const TEST_USERNAME = 'test';
|
const TEST_USERNAME = process.env.TEST_USERNAME || 'test';
|
||||||
const TEST_PASSWORD = 'test';
|
const TEST_PASSWORD = process.env.TEST_PASSWORD || 'test';
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 检查是否是测试账号 - 支持通过username或email字段登录
|
// 检查是否是测试账号 - 支持通过username或email字段登录
|
||||||
const usernameField = credentials?.username || credentials?.email;
|
const usernameField = credentials?.username || credentials?.email;
|
||||||
if (usernameField === TEST_USERNAME && credentials?.password === TEST_PASSWORD) {
|
if (process.env.NODE_ENV !== 'production' &&
|
||||||
|
usernameField === TEST_USERNAME &&
|
||||||
|
credentials?.password === TEST_PASSWORD) {
|
||||||
// 返回模拟的测试用户数据
|
// 返回模拟的测试用户数据
|
||||||
return {
|
return {
|
||||||
id: 'test_user_1',
|
id: 'test_user_1',
|
||||||
@@ -41,27 +80,42 @@ export const authOptions: AuthOptions = {
|
|||||||
email: 'test@test.com',
|
email: 'test@test.com',
|
||||||
minecraftUsername: 'SteveTest'
|
minecraftUsername: 'SteveTest'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正常的API登录流程
|
// 验证输入
|
||||||
const response = await axios.post(`${API_URL}/auth/login`, {
|
if (!usernameField || !credentials?.password) {
|
||||||
|
console.error('用户名/邮箱和密码不能为空');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 正常的API登录流程 - 使用apiClient
|
||||||
|
const response = await apiClient.post('/auth/login', {
|
||||||
username: credentials?.username,
|
username: credentials?.username,
|
||||||
|
email: credentials?.email,
|
||||||
password: credentials?.password
|
password: credentials?.password
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.data && response.data.user) {
|
if (response.data && response.data.user) {
|
||||||
return {
|
return {
|
||||||
id: response.data.user.id,
|
id: response.data.user.id,
|
||||||
name: response.data.user.username,
|
name: response.data.user.name || response.data.user.username,
|
||||||
email: response.data.user.email
|
email: response.data.user.email,
|
||||||
|
minecraftUsername: response.data.user.minecraftUsername
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('认证失败:', error);
|
console.error('认证失败:', error);
|
||||||
|
// 区分不同类型的错误,提供更好的错误反馈
|
||||||
|
if (error instanceof axios.AxiosError) {
|
||||||
|
if (error.response?.status === 401) {
|
||||||
|
console.error('用户名或密码错误');
|
||||||
|
} else if (error.response?.status === 400) {
|
||||||
|
console.error('登录信息不完整或格式错误');
|
||||||
|
} else if (!error.response) {
|
||||||
|
console.error('无法连接到认证服务器');
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user