-
Notifications
You must be signed in to change notification settings - Fork 213
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Implement role-based access control and enhance permissions system
- Loading branch information
1 parent
e815d1b
commit 5a7c177
Showing
22 changed files
with
1,888 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { auth } from "@/lib/auth"; | ||
import { createDb } from "@/lib/db"; | ||
import { roles, userRoles } from "@/lib/schema"; | ||
import { ROLES } from "@/lib/permissions"; | ||
import { eq } from "drizzle-orm"; | ||
|
||
export const runtime = "edge"; | ||
|
||
export async function GET() { | ||
const session = await auth(); | ||
if (!session?.user?.id) { | ||
return Response.json({ error: "未授权" }, { status: 401 }); | ||
} | ||
|
||
const db = createDb(); | ||
|
||
const emperorRole = await db.query.roles.findFirst({ | ||
where: eq(roles.name, ROLES.EMPEROR), | ||
with: { | ||
userRoles: true, | ||
}, | ||
}); | ||
|
||
if (emperorRole && emperorRole.userRoles.length > 0) { | ||
return Response.json({ error: "已存在皇帝, 谋反将被处死" }, { status: 400 }); | ||
} | ||
|
||
try { | ||
let roleId = emperorRole?.id; | ||
if (!roleId) { | ||
const [newRole] = await db.insert(roles) | ||
.values({ | ||
name: ROLES.EMPEROR, | ||
description: "皇帝(网站所有者)", | ||
}) | ||
.returning({ id: roles.id }); | ||
roleId = newRole.id; | ||
} | ||
|
||
await db.insert(userRoles) | ||
.values({ | ||
userId: session.user.id, | ||
roleId, | ||
}); | ||
|
||
return Response.json({ message: "登基成功,你已成为皇帝" }); | ||
} catch (error) { | ||
console.error("Failed to initialize emperor:", error); | ||
return Response.json( | ||
{ error: "登基称帝失败" }, | ||
{ status: 500 } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { createDb } from "@/lib/db"; | ||
import { roles, userRoles } from "@/lib/schema"; | ||
import { eq } from "drizzle-orm"; | ||
import { ROLES } from "@/lib/permissions"; | ||
|
||
export const runtime = "edge"; | ||
|
||
export async function POST(request: Request) { | ||
try { | ||
const { userId, roleName } = await request.json() as { userId: string, roleName: string }; | ||
if (!userId || !roleName) { | ||
return Response.json( | ||
{ error: "缺少必要参数" }, | ||
{ status: 400 } | ||
); | ||
} | ||
|
||
if (roleName !== ROLES.KNIGHT) { | ||
return Response.json( | ||
{ error: "角色不合法" }, | ||
{ status: 400 } | ||
); | ||
} | ||
|
||
const db = createDb(); | ||
|
||
let targetRole = await db.query.roles.findFirst({ | ||
where: eq(roles.name, roleName), | ||
}); | ||
|
||
if (!targetRole) { | ||
const [newRole] = await db.insert(roles) | ||
.values({ | ||
name: roleName, | ||
description: "高级用户", | ||
}) | ||
.returning(); | ||
targetRole = newRole; | ||
} | ||
|
||
await db.delete(userRoles) | ||
.where(eq(userRoles.userId, userId)); | ||
|
||
await db.insert(userRoles) | ||
.values({ | ||
userId, | ||
roleId: targetRole.id, | ||
}); | ||
|
||
return Response.json({ success: true }); | ||
} catch (error) { | ||
console.error("Failed to promote user:", error); | ||
return Response.json( | ||
{ error: "升级用户失败" }, | ||
{ status: 500 } | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
import { createDb } from "@/lib/db" | ||
import { userRoles, users } from "@/lib/schema" | ||
import { eq } from "drizzle-orm" | ||
|
||
export const runtime = "edge" | ||
|
||
export async function GET(request: Request) { | ||
const url = new URL(request.url) | ||
const email = url.searchParams.get('email') | ||
|
||
if (!email) { | ||
return Response.json( | ||
{ error: "邮箱地址不能为空" }, | ||
{ status: 400 } | ||
) | ||
} | ||
|
||
const db = createDb() | ||
|
||
const user = await db.query.users.findFirst({ | ||
where: eq(users.email, email), | ||
}) | ||
|
||
if (!user) { | ||
return Response.json({ user: null }) | ||
} | ||
|
||
const userRole = await db.query.userRoles.findFirst({ | ||
where: eq(userRoles.userId, user.id), | ||
with: { | ||
role: true | ||
} | ||
}) | ||
|
||
return Response.json({ | ||
user: { | ||
id: user.id, | ||
name: user.name, | ||
email: user.email, | ||
role: userRole?.role.name | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
"use client" | ||
|
||
import { Button } from "@/components/ui/button" | ||
import { useRouter } from "next/navigation" | ||
|
||
export function NoPermissionDialog() { | ||
const router = useRouter() | ||
|
||
return ( | ||
<div className="fixed inset-0 bg-background/50 backdrop-blur-sm z-50"> | ||
<div className="fixed left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] w-[90%] max-w-md"> | ||
<div className="bg-background border-2 border-primary/20 rounded-lg p-6 md:p-12 shadow-lg"> | ||
<div className="text-center space-y-4"> | ||
<h1 className="text-xl md:text-2xl font-bold">权限不足</h1> | ||
<p className="text-sm md:text-base text-muted-foreground">你没有权限访问此页面,请联系网站皇帝授权</p> | ||
<Button | ||
onClick={() => router.push("/")} | ||
className="mt-4 w-full md:w-auto" | ||
> | ||
返回首页 | ||
</Button> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.