feat: 重构用户角色系统为管理员标识
- 将用户角色字段从role改为is_admin布尔值 - 更新相关API权限检查逻辑 - 修改数据库schema和迁移文件 - 调整前端用户显示逻辑 - 添加API响应工具函数 - 优化权限检查中间件 - 重构英雄组件为原子组件
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
import { NextResponse } from 'next/server';
|
||||
|
||||
export type ErrorCode =
|
||||
| 'UNAUTHORIZED'
|
||||
| 'FORBIDDEN'
|
||||
| 'NOT_FOUND'
|
||||
| 'VALIDATION_ERROR'
|
||||
| 'INTERNAL_ERROR'
|
||||
| 'BAD_REQUEST';
|
||||
|
||||
export interface ApiError {
|
||||
error: string;
|
||||
code: ErrorCode;
|
||||
details?: Record<string, unknown>;
|
||||
}
|
||||
|
||||
const ERROR_MESSAGES: Record<ErrorCode, string> = {
|
||||
UNAUTHORIZED: '未授权,请先登录',
|
||||
FORBIDDEN: '无权限执行此操作',
|
||||
NOT_FOUND: '请求的资源不存在',
|
||||
VALIDATION_ERROR: '数据验证失败',
|
||||
INTERNAL_ERROR: '服务器内部错误',
|
||||
BAD_REQUEST: '请求参数错误',
|
||||
};
|
||||
|
||||
export function unauthorized(message?: string): NextResponse<ApiError> {
|
||||
return NextResponse.json(
|
||||
{ error: message || ERROR_MESSAGES.UNAUTHORIZED, code: 'UNAUTHORIZED' },
|
||||
{ status: 401 }
|
||||
);
|
||||
}
|
||||
|
||||
export function forbidden(message?: string): NextResponse<ApiError> {
|
||||
return NextResponse.json(
|
||||
{ error: message || ERROR_MESSAGES.FORBIDDEN, code: 'FORBIDDEN' },
|
||||
{ status: 403 }
|
||||
);
|
||||
}
|
||||
|
||||
export function notFound(message?: string): NextResponse<ApiError> {
|
||||
return NextResponse.json(
|
||||
{ error: message || ERROR_MESSAGES.NOT_FOUND, code: 'NOT_FOUND' },
|
||||
{ status: 404 }
|
||||
);
|
||||
}
|
||||
|
||||
export function validationError(message: string, details?: Record<string, unknown>): NextResponse<ApiError> {
|
||||
return NextResponse.json(
|
||||
{ error: message, code: 'VALIDATION_ERROR', details },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
export function badRequest(message: string): NextResponse<ApiError> {
|
||||
return NextResponse.json(
|
||||
{ error: message, code: 'BAD_REQUEST' },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
export function internalError(message = '服务器错误'): NextResponse<ApiError> {
|
||||
console.error(message);
|
||||
return NextResponse.json(
|
||||
{ error: ERROR_MESSAGES.INTERNAL_ERROR, code: 'INTERNAL_ERROR' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
|
||||
export function success<T>(data: T, status = 200): NextResponse<T> {
|
||||
return NextResponse.json(data, { status });
|
||||
}
|
||||
|
||||
export function handleApiError(error: unknown): NextResponse<ApiError> {
|
||||
console.error('API Error:', error);
|
||||
|
||||
if (error instanceof Error) {
|
||||
if (error.message.includes('未授权')) {
|
||||
return unauthorized(error.message);
|
||||
}
|
||||
if (error.message.includes('无权限')) {
|
||||
return forbidden(error.message);
|
||||
}
|
||||
if (error.message.includes('不存在')) {
|
||||
return notFound(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
return internalError();
|
||||
}
|
||||
+3
-3
@@ -42,7 +42,7 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
id: user.id,
|
||||
email: user.email,
|
||||
name: user.name,
|
||||
role: user.role,
|
||||
isAdmin: user.isAdmin,
|
||||
};
|
||||
},
|
||||
}),
|
||||
@@ -51,14 +51,14 @@ export const { handlers, signIn, signOut, auth } = NextAuth({
|
||||
async jwt({ token, user }) {
|
||||
if (user) {
|
||||
token.id = user.id;
|
||||
token.role = user.role;
|
||||
token.isAdmin = user.isAdmin;
|
||||
}
|
||||
return token;
|
||||
},
|
||||
async session({ session, token }) {
|
||||
if (session.user) {
|
||||
session.user.id = token.id as string;
|
||||
session.user.role = token.role as string;
|
||||
session.user.isAdmin = token.isAdmin as boolean;
|
||||
}
|
||||
return session;
|
||||
},
|
||||
|
||||
@@ -1,38 +1,37 @@
|
||||
import { auth } from '../auth';
|
||||
import { hasPermission, Role, Resource, Action } from './permissions';
|
||||
import { isAdminUser } from './permissions';
|
||||
|
||||
export async function checkPermission(
|
||||
resource: Resource,
|
||||
action: Action
|
||||
): Promise<{ allowed: boolean; userId?: string; role?: Role }> {
|
||||
export async function checkIsAdmin(): Promise<{ isAdmin: boolean; userId?: string }> {
|
||||
const session = await auth();
|
||||
|
||||
if (!session || !session.user) {
|
||||
return { allowed: false };
|
||||
return { isAdmin: false };
|
||||
}
|
||||
|
||||
const userRole = session.user.role as Role;
|
||||
const allowed = hasPermission(userRole, resource, action);
|
||||
const isAdmin = isAdminUser(session.user.isAdmin as boolean | undefined);
|
||||
|
||||
return {
|
||||
allowed,
|
||||
isAdmin,
|
||||
userId: session.user.id,
|
||||
role: userRole,
|
||||
};
|
||||
}
|
||||
|
||||
export async function requirePermission(
|
||||
resource: Resource,
|
||||
action: Action
|
||||
): Promise<{ userId: string; role: Role }> {
|
||||
const result = await checkPermission(resource, action);
|
||||
export async function requireAdmin(): Promise<{ userId: string }> {
|
||||
const result = await checkIsAdmin();
|
||||
|
||||
if (!result.allowed) {
|
||||
if (!result.isAdmin) {
|
||||
throw new Error('无权限执行此操作');
|
||||
}
|
||||
|
||||
return {
|
||||
userId: result.userId!,
|
||||
role: result.role!,
|
||||
};
|
||||
}
|
||||
|
||||
export async function getAdminUserId(): Promise<string | null> {
|
||||
const session = await auth();
|
||||
if (!session?.user) {
|
||||
return null;
|
||||
}
|
||||
return session.user.id;
|
||||
}
|
||||
|
||||
@@ -1,38 +1,3 @@
|
||||
export const PERMISSIONS = {
|
||||
admin: {
|
||||
content: ['create', 'read', 'update', 'delete', 'publish'],
|
||||
config: ['read', 'update'],
|
||||
users: ['create', 'read', 'update', 'delete'],
|
||||
logs: ['read'],
|
||||
},
|
||||
editor: {
|
||||
content: ['create', 'read', 'update', 'publish'],
|
||||
config: ['read'],
|
||||
users: [],
|
||||
logs: ['read'],
|
||||
},
|
||||
viewer: {
|
||||
content: ['read'],
|
||||
config: ['read'],
|
||||
users: [],
|
||||
logs: [],
|
||||
},
|
||||
} as const;
|
||||
|
||||
export type Role = keyof typeof PERMISSIONS;
|
||||
export type Resource = keyof typeof PERMISSIONS.admin;
|
||||
export type Action = 'create' | 'read' | 'update' | 'delete' | 'publish';
|
||||
|
||||
export function hasPermission(
|
||||
role: Role,
|
||||
resource: Resource,
|
||||
action: Action
|
||||
): boolean {
|
||||
const permissions = PERMISSIONS[role];
|
||||
if (!permissions) return false;
|
||||
|
||||
const resourcePermissions = permissions[resource];
|
||||
if (!resourcePermissions) return false;
|
||||
|
||||
return resourcePermissions.includes(action as never);
|
||||
export function isAdminUser(isAdmin: boolean | undefined): boolean {
|
||||
return isAdmin === true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user