"use client" import * as React from "react" import Link from "next/link" import {useMutation, useQuery, useQueryClient} from "@tanstack/react-query" import {motion} from "motion/react" import {AlertTriangle, Check, Copy, Info, Key, Loader2, Plus, RefreshCw, Shield, Trash2} from "lucide-react" import {Button} from "@/components/ui/button" import {Card, CardContent, CardDescription, CardHeader, CardTitle} from "@/components/ui/card" import {Input} from "@/components/ui/input" import {Label} from "@/components/ui/label" import {Badge} from "@/components/ui/badge" import {Switch} from "@/components/ui/switch" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog" import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, } from "@/components/ui/breadcrumb" import {UserService} from "@/lib/services/user" import type {CreateTokenResponse} from "@/lib/services/user" import {useAuth} from "@/components/providers/auth-provider" import {toast} from "sonner" export function AccessTokenMain() { const { user } = useAuth() const queryClient = useQueryClient() const [createDialogOpen, setCreateDialogOpen] = React.useState(false) const [viewDialogOpen, setViewDialogOpen] = React.useState(false) const [tokenName, setTokenName] = React.useState("") const [tokenIsAdmin, setTokenIsAdmin] = React.useState(false) const [copiedId, setCopiedId] = React.useState(null) const [newCreatedToken, setNewCreatedToken] = React.useState(null) // 获取 Token 列表 const accessTokensQuery = useQuery({ queryKey: ["user", "access-tokens"], queryFn: () => UserService.getAccessTokens(), }) // 创建 Token const createTokenMutation = useMutation({ mutationFn: ({ name, isAdmin }: { name: string; isAdmin: boolean }) => UserService.createAccessToken(name, isAdmin), onSuccess: (data) => { setNewCreatedToken(data) setTokenName("") setTokenIsAdmin(false) setCreateDialogOpen(false) setViewDialogOpen(true) void queryClient.invalidateQueries({ queryKey: ["user", "access-tokens"] }) toast.success("访问令牌创建成功") }, onError: (error: Error) => { toast.error(error.message || "创建访问令牌失败") }, }) // 删除 Token const deleteTokenMutation = useMutation({ mutationFn: (id: number) => UserService.deleteAccessToken(id), onSuccess: () => { void queryClient.invalidateQueries({ queryKey: ["user", "access-tokens"] }) toast.success("访问令牌已撤销") }, onError: (error: Error) => { toast.error(error.message || "删除访问令牌失败") }, }) // 轮换 Token const rotateTokenMutation = useMutation({ mutationFn: (id: number) => UserService.rotateAccessToken(id), onSuccess: (data) => { setNewCreatedToken(data) setViewDialogOpen(true) void queryClient.invalidateQueries({ queryKey: ["user", "access-tokens"] }) toast.success("访问令牌轮换成功,旧密钥已失效") }, onError: (error: Error) => { toast.error(error.message || "轮换访问令牌失败") }, }) const handleCreateToken = (e: React.FormEvent) => { e.preventDefault() if (!tokenName.trim()) { toast.error("请输入令牌名称") return } createTokenMutation.mutate({ name: tokenName.trim(), isAdmin: tokenIsAdmin }) } const handleDeleteToken = (id: number, name: string) => { if (window.confirm(`确定要删除并撤销令牌「${name}」吗?删除后此令牌将立即失效且不可恢复。`)) { deleteTokenMutation.mutate(id) } } const handleRotateToken = (id: number, name: string) => { if (window.confirm(`确定要轮换令牌「${name}」的密钥吗?轮换后系统将生成全新密钥,原令牌密钥将立即失效。`)) { rotateTokenMutation.mutate(id) } } const handleCopyText = async (text: string, id: number) => { try { await navigator.clipboard.writeText(text) setCopiedId(id) toast.success("复制成功") setTimeout(() => setCopiedId(null), 2000) } catch { toast.error("复制失败") } } const formatDate = (dateStr?: string) => { if (!dateStr) return "未使用" return new Date(dateStr).toLocaleString("zh-CN", { year: "numeric", month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", }) } return (
设置 访问令牌

个人访问令牌 (AccessToken)

管理您的 API 访问密钥,用于开发或第三方工具直接调用系统 API

{/* 安全警告提示 */}
安全提示:

访问令牌具有您账户的完整接口调用权限。为了您的账户与资产安全,请切勿通过任何代码库提交、即时通讯工具或公共媒介泄露此令牌。推荐按需创建,不使用时及时撤销。

活动令牌列表 当前可用的所有访问令牌 {accessTokensQuery.isPending ? (
) : (accessTokensQuery.data ?? []).length > 0 ? (
{(accessTokensQuery.data ?? []).map((token) => (
{token.name} {token.is_admin ? ( 管理员 ) : ( 用户令牌 )}
{token.masked_token}
创建于: {formatDate(token.created_at)}
))}
) : (
您当前暂无生成任何访问令牌
)}
{/* 创建令牌 Dialog */}
生成新令牌 请为新的访问令牌设置一个易于识别的名称,以便将来管理。
setTokenName(e.target.value)} disabled={createTokenMutation.isPending} className="rounded-xl border border-dashed focus:border-indigo-500 focus:ring-0 focus-visible:ring-0" />
{user?.is_admin && (

开启后此令牌可访问 /admin/** 管理端点,默认关闭

)}
{/* 明文 Token 显示 Dialog (仅显示一次) */} { if (!open) { setNewCreatedToken(null) setViewDialogOpen(false) } }}> 令牌密钥已就绪 {newCreatedToken && (
{/* 明文 Token 文本框 */}
{newCreatedToken.token}
{/* 强提示 */}
重要提示:

这是您唯一一次能够查看此访问令牌明文密钥的机会。请立即将其复制并安全地保存。

)}
) }