feat: 重构用户角色系统为管理员标识

- 将用户角色字段从role改为is_admin布尔值
- 更新相关API权限检查逻辑
- 修改数据库schema和迁移文件
- 调整前端用户显示逻辑
- 添加API响应工具函数
- 优化权限检查中间件
- 重构英雄组件为原子组件
This commit is contained in:
张翔
2026-03-12 20:45:43 +08:00
parent b207bfa7af
commit f357330ba8
22 changed files with 1078 additions and 552 deletions
+39 -61
View File
@@ -1,8 +1,8 @@
import { NextRequest, NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { auth } from '@/lib/auth';
import { hasPermission } from '@/lib/auth/permissions';
import { checkIsAdmin } from '@/lib/auth/check-permission';
import { forbidden, notFound, success, handleApiError } from '@/lib/api-response';
import { eq } from 'drizzle-orm';
import bcrypt from 'bcryptjs';
@@ -11,26 +11,24 @@ export async function GET(
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
const { isAdmin, userId } = await checkIsAdmin();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const userRole = session.user.role as 'admin' | 'editor' | 'viewer';
if (!hasPermission(userRole, 'users', 'read')) {
return NextResponse.json({ error: '无权限' }, { status: 403 });
if (!isAdmin) {
return forbidden();
}
const { id } = await params;
if (id !== userId) {
return forbidden('只能查看自己的信息');
}
const user = await db
.select({
id: users.id,
email: users.email,
name: users.name,
role: users.role,
isAdmin: users.isAdmin,
createdAt: users.createdAt,
updatedAt: users.updatedAt,
})
@@ -39,13 +37,12 @@ export async function GET(
.limit(1);
if (user.length === 0) {
return NextResponse.json({ error: '用户不存在' }, { status: 404 });
return notFound('用户不存在');
}
return NextResponse.json({ user: user[0] });
return success({ user: user[0] });
} catch (error) {
console.error('获取用户详情失败:', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
return handleApiError(error);
}
}
@@ -54,21 +51,20 @@ export async function PUT(
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
const { isAdmin, userId } = await checkIsAdmin();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const userRole = session.user.role as 'admin' | 'editor' | 'viewer';
if (!hasPermission(userRole, 'users', 'update')) {
return NextResponse.json({ error: '无权限' }, { status: 403 });
if (!isAdmin) {
return forbidden();
}
const { id } = await params;
if (id !== userId) {
return forbidden('只能修改自己的信息');
}
const body = await request.json();
const { email, name, password, role } = body;
const { email, name, password } = body;
const existingUser = await db
.select()
@@ -77,16 +73,15 @@ export async function PUT(
.limit(1);
if (existingUser.length === 0) {
return NextResponse.json({ error: '用户不存在' }, { status: 404 });
return notFound('用户不存在');
}
const updateData: Record<string, any> = {
const updateData: Record<string, unknown> = {
updatedAt: new Date(),
};
if (email) updateData.email = email;
if (name) updateData.name = name;
if (role) updateData.role = role;
if (password) {
updateData.passwordHash = await bcrypt.hash(password, 10);
}
@@ -97,18 +92,16 @@ export async function PUT(
.where(eq(users.id, id))
.returning();
return NextResponse.json({
return success({
user: {
id: updated[0]!.id,
email: updated[0]!.email,
name: updated[0]!.name,
role: updated[0]!.role,
createdAt: updated[0]!.createdAt,
}
isAdmin: updated[0]!.isAdmin,
},
});
} catch (error) {
console.error('更新用户失败:', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
return handleApiError(error);
}
}
@@ -117,39 +110,24 @@ export async function DELETE(
{ params }: { params: Promise<{ id: string }> }
) {
try {
const session = await auth();
const { isAdmin, userId } = await checkIsAdmin();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const userRole = session.user.role as 'admin' | 'editor' | 'viewer';
if (!hasPermission(userRole, 'users', 'delete')) {
return NextResponse.json({ error: '无权限' }, { status: 403 });
if (!isAdmin) {
return forbidden();
}
const { id } = await params;
if (session.user.id === id) {
return NextResponse.json({ error: '不能删除自己的账号' }, { status: 400 });
if (id !== userId) {
return forbidden('不能删除其他用户');
}
const existingUser = await db
.select()
.from(users)
.where(eq(users.id, id))
.limit(1);
await db
.delete(users)
.where(eq(users.id, id));
if (existingUser.length === 0) {
return NextResponse.json({ error: '用户不存在' }, { status: 404 });
}
await db.delete(users).where(eq(users.id, id));
return NextResponse.json({ success: true });
return success({ success: true });
} catch (error) {
console.error('删除用户失败:', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
return handleApiError(error);
}
}
+11 -80
View File
@@ -1,24 +1,16 @@
import { NextRequest, NextResponse } from 'next/server';
import { NextRequest } from 'next/server';
import { db } from '@/db';
import { users } from '@/db/schema';
import { auth } from '@/lib/auth';
import { hasPermission } from '@/lib/auth/permissions';
import { eq } from 'drizzle-orm';
import { nanoid } from 'nanoid';
import bcrypt from 'bcryptjs';
import { checkIsAdmin } from '@/lib/auth/check-permission';
import { forbidden, success, handleApiError } from '@/lib/api-response';
import { desc } from 'drizzle-orm';
export async function GET(_request: NextRequest) {
try {
const session = await auth();
const { isAdmin } = await checkIsAdmin();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const userRole = session.user.role as 'admin' | 'editor' | 'viewer';
if (!hasPermission(userRole, 'users', 'read')) {
return NextResponse.json({ error: '无权限' }, { status: 403 });
if (!isAdmin) {
return forbidden();
}
const allUsers = await db
@@ -26,77 +18,16 @@ export async function GET(_request: NextRequest) {
id: users.id,
email: users.email,
name: users.name,
role: users.role,
isAdmin: users.isAdmin,
createdAt: users.createdAt,
updatedAt: users.updatedAt,
})
.from(users)
.orderBy(users.createdAt);
.orderBy(desc(users.createdAt));
return NextResponse.json({ users: allUsers });
return success({ users: allUsers });
} catch (error) {
console.error('获取用户列表失败:', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
}
}
export async function POST(request: NextRequest) {
try {
const session = await auth();
if (!session?.user) {
return NextResponse.json({ error: '未授权' }, { status: 401 });
}
const userRole = session.user.role as 'admin' | 'editor' | 'viewer';
if (!hasPermission(userRole, 'users', 'create')) {
return NextResponse.json({ error: '无权限' }, { status: 403 });
}
const body = await request.json();
const { email, name, password, role } = body;
if (!email || !name || !password || !role) {
return NextResponse.json({ error: '缺少必填字段' }, { status: 400 });
}
const existingUser = await db
.select()
.from(users)
.where(eq(users.email, email))
.limit(1);
if (existingUser.length > 0) {
return NextResponse.json({ error: '邮箱已被使用' }, { status: 400 });
}
const hashedPassword = await bcrypt.hash(password, 10);
const newUser = await db
.insert(users)
.values({
id: nanoid(),
email,
name,
passwordHash: hashedPassword,
role,
createdAt: new Date(),
updatedAt: new Date(),
})
.returning();
return NextResponse.json({
user: {
id: newUser[0]!.id,
email: newUser[0]!.email,
name: newUser[0]!.name,
role: newUser[0]!.role,
createdAt: newUser[0]!.createdAt,
}
});
} catch (error) {
console.error('创建用户失败:', error);
return NextResponse.json({ error: '服务器错误' }, { status: 500 });
return handleApiError(error);
}
}