'use client' import { useState } from 'react' import useSWR from 'swr' import { Plus, Loader2, ChevronLeft, ChevronRight, Trash2, Copy, Check, AlertTriangle, } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Label } from '@/components/ui/label' import { Badge } from '@/components/ui/badge' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription, } from '@/components/ui/dialog' import { api } from '@/lib/api-client' import { ErrorBanner, EmptyState } from '@/components/ui/state' import { ApiRequestError } from '@/lib/api-client' import { formatDate, getSwrErrorMessage } from '@/lib/utils' import { TableSkeleton } from '@/components/ui/skeleton' import type { TokenInfo } from '@/lib/types' const PAGE_SIZE = 20 const allPermissions = [ { key: 'chat', label: '对话' }, { key: 'relay', label: '中转' }, { key: 'admin', label: '管理' }, ] export default function ApiKeysPage() { const [page, setPage] = useState(1) const [mutationError, setMutationError] = useState('') const { data, error: swrError, isLoading, mutate } = useSWR( ['tokens', page], () => api.tokens.list({ page, page_size: PAGE_SIZE }), ) const tokens = data?.items ?? [] const total = data?.total ?? 0 const error = getSwrErrorMessage(swrError) || mutationError // 创建 Dialog const [createOpen, setCreateOpen] = useState(false) const [createForm, setCreateForm] = useState({ name: '', expires_days: '', permissions: ['chat'] as string[] }) const [creating, setCreating] = useState(false) // 创建成功显示 token const [createdToken, setCreatedToken] = useState(null) const [copied, setCopied] = useState(false) // 撤销确认 const [revokeTarget, setRevokeTarget] = useState(null) const [revoking, setRevoking] = useState(false) const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE)) function togglePermission(perm: string) { setCreateForm((prev) => ({ ...prev, permissions: prev.permissions.includes(perm) ? prev.permissions.filter((p) => p !== perm) : [...prev.permissions, perm], })) } async function handleCreate() { if (!createForm.name.trim() || createForm.permissions.length === 0) return setCreating(true) try { const payload = { name: createForm.name.trim(), expires_days: createForm.expires_days ? parseInt(createForm.expires_days, 10) : undefined, permissions: createForm.permissions, } const res = await api.tokens.create(payload) setCreateOpen(false) setCreatedToken(res) setCreateForm({ name: '', expires_days: '', permissions: ['chat'] }) mutate() } catch (err) { if (err instanceof ApiRequestError) setMutationError(err.body.message) } finally { setCreating(false) } } async function handleRevoke() { if (!revokeTarget) return setRevoking(true) try { await api.tokens.revoke(revokeTarget.id) setRevokeTarget(null) mutate() } catch (err) { if (err instanceof ApiRequestError) setMutationError(err.body.message) } finally { setRevoking(false) } } async function copyToken() { if (!createdToken?.token) return try { await navigator.clipboard.writeText(createdToken.token) setCopied(true) setTimeout(() => setCopied(false), 2000) } catch { // Fallback const textarea = document.createElement('textarea') textarea.value = createdToken.token document.body.appendChild(textarea) textarea.select() document.execCommand('copy') document.body.removeChild(textarea) setCopied(true) setTimeout(() => setCopied(false), 2000) } } return (
{error && setMutationError('')} />} {isLoading ? ( ) : error ? null : tokens.length === 0 ? ( ) : ( <> 名称 前缀 权限 最后使用 过期时间 创建时间 操作 {tokens.map((t) => ( {t.name} {t.token_prefix}...
{t.permissions.map((p) => ( {p} ))}
{t.last_used_at ? formatDate(t.last_used_at) : '未使用'} {t.expires_at ? formatDate(t.expires_at) : '永不过期'} {formatDate(t.created_at)}
))}

第 {page} 页 / 共 {totalPages} 页 ({total} 条)

)} {/* 创建 Dialog */} 新建 API 密钥 创建新的 API 密钥用于接口调用
setCreateForm({ ...createForm, name: e.target.value })} placeholder="例如: 生产环境" />
setCreateForm({ ...createForm, expires_days: e.target.value })} placeholder="365" />
{allPermissions.map((perm) => ( ))}
{/* 创建成功 Dialog */} setCreatedToken(null)}> 密钥已创建 请立即复制并安全保存此密钥,关闭后将无法再次查看完整密钥。

完整密钥

{createdToken?.token}

此密钥仅显示一次。请确保已保存到安全的位置。
{/* 撤销确认 */} setRevokeTarget(null)}> 确认撤销 确定要撤销密钥 "{revokeTarget?.name}" 吗?使用此密钥的应用将立即失去访问权限。
) }