docs(skills): 添加 Better Auth 最佳实践与安全配置指南
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
"use client";
|
||||
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState } from "react";
|
||||
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuLabel,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { authClient } from "@/lib/auth-client";
|
||||
import { MoreHorizontal, ShieldCheck, User, Ban, CircleCheck, Loader2 } from "lucide-react";
|
||||
|
||||
type UserRow = {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
role: string;
|
||||
banned: boolean;
|
||||
};
|
||||
|
||||
export function UserActions({ user }: { user: UserRow }) {
|
||||
const router = useRouter();
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
async function setRole(role: "admin" | "user") {
|
||||
setLoading(true);
|
||||
const { error } = await authClient.admin.setRole({
|
||||
userId: user.id,
|
||||
role,
|
||||
});
|
||||
setLoading(false);
|
||||
if (error) {
|
||||
console.error("设置角色失败:", error.message);
|
||||
return;
|
||||
}
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
async function toggleBan() {
|
||||
setLoading(true);
|
||||
if (user.banned) {
|
||||
const { error } = await authClient.admin.unbanUser({ userId: user.id });
|
||||
setLoading(false);
|
||||
if (error) {
|
||||
console.error("解封失败:", error.message);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const { error } = await authClient.admin.banUser({
|
||||
userId: user.id,
|
||||
banReason: "管理员手动封禁",
|
||||
});
|
||||
setLoading(false);
|
||||
if (error) {
|
||||
console.error("封禁失败:", error.message);
|
||||
return;
|
||||
}
|
||||
}
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
render={
|
||||
<Button variant="ghost" size="icon-sm" disabled={loading} />
|
||||
}
|
||||
>
|
||||
{loading ? (
|
||||
<Loader2 className="size-4 animate-spin" />
|
||||
) : (
|
||||
<MoreHorizontal className="size-4" />
|
||||
)}
|
||||
<span className="sr-only">操作菜单</span>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-40">
|
||||
<DropdownMenuLabel>设置角色</DropdownMenuLabel>
|
||||
<DropdownMenuItem onClick={() => setRole("admin")}>
|
||||
<ShieldCheck className="size-4" />
|
||||
管理员
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={() => setRole("user")}>
|
||||
<User className="size-4" />
|
||||
普通用户
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem onClick={toggleBan}>
|
||||
{user.banned ? (
|
||||
<>
|
||||
<CircleCheck className="size-4" />
|
||||
解除封禁
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<Ban className="size-4" />
|
||||
封禁用户
|
||||
</>
|
||||
)}
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user