import { useState } from 'react' import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { Button, message, Tag, Modal, Form, Input, InputNumber, Select, Space, Popconfirm, Typography } from 'antd' import { PlusOutlined, CopyOutlined } from '@ant-design/icons' import { ProTable } from '@ant-design/pro-components' import type { ProColumns } from '@ant-design/pro-components' import { apiKeyService } from '@/services/api-keys' import type { TokenInfo } from '@/types' const { Text, Paragraph } = Typography const PERMISSION_OPTIONS = [ { label: 'Relay Chat', value: 'relay:use' }, { label: 'Knowledge Read', value: 'knowledge:read' }, { label: 'Knowledge Write', value: 'knowledge:write' }, { label: 'Agent Read', value: 'agent:read' }, { label: 'Agent Write', value: 'agent:write' }, ] export default function ApiKeys() { const queryClient = useQueryClient() const [form] = Form.useForm() const [createOpen, setCreateOpen] = useState(false) const [newToken, setNewToken] = useState(null) const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(20) const { data, isLoading } = useQuery({ queryKey: ['api-keys', page, pageSize], queryFn: ({ signal }) => apiKeyService.list({ page, page_size: pageSize }, signal), }) const createMutation = useMutation({ mutationFn: (values: { name: string; expires_days?: number; permissions: string[] }) => apiKeyService.create(values), onSuccess: (result: TokenInfo) => { message.success('API 密钥创建成功') if (result.token) { setNewToken(result.token) } queryClient.invalidateQueries({ queryKey: ['api-keys'] }) form.resetFields() }, onError: (err: Error) => message.error(err.message || '创建失败'), }) const revokeMutation = useMutation({ mutationFn: (id: string) => apiKeyService.revoke(id), onSuccess: () => { message.success('密钥已吊销') queryClient.invalidateQueries({ queryKey: ['api-keys'] }) }, onError: (err: Error) => message.error(err.message || '吊销失败'), }) const handleCreate = async () => { const values = await form.validateFields() createMutation.mutate(values) } const columns: ProColumns[] = [ { title: '名称', dataIndex: 'name', width: 180 }, { title: '前缀', dataIndex: 'token_prefix', width: 120, render: (val: string) => {val}..., }, { title: '权限', dataIndex: 'permissions', width: 240, render: (perms: string[]) => perms?.map((p) => {p}) || '-', }, { title: '最后使用', dataIndex: 'last_used_at', width: 180, render: (val: string) => (val ? new Date(val).toLocaleString() : 从未使用), }, { title: '过期时间', dataIndex: 'expires_at', width: 180, render: (val: string) => val ? new Date(val).toLocaleString() : 永不过期, }, { title: '创建时间', dataIndex: 'created_at', width: 180, render: (val: string) => new Date(val).toLocaleString(), }, { title: '操作', width: 100, render: (_: unknown, record: TokenInfo) => ( revokeMutation.mutate(record.id)} > ), }, ] return (
columns={columns} dataSource={data?.items || []} loading={isLoading} rowKey="id" search={false} pagination={{ current: page, pageSize, total: data?.total || 0, onChange: (p, ps) => { setPage(p); setPageSize(ps) }, }} toolBarRender={() => [ , ]} /> { setCreateOpen(false); setNewToken(null); form.resetFields() }} confirmLoading={createMutation.isPending} destroyOnHidden > {newToken ? (
请立即复制密钥,关闭后将无法再次查看。 {newToken}
) : (