import { useMemo, useState, type FormEvent, type ReactNode } from 'react'; import { Copy, CreditCard, KeyRound, ListChecks, Plus, ShieldCheck, Trash2, UserRound } from 'lucide-react'; import type { GatewayAccessRuleBatchRequest, GatewayApiKey, IntegrationPlatform, PlatformModel } from '@easyai-ai-gateway/contracts'; import type { ConsoleData } from '../app-state'; import { EntityTable } from '../components/EntityTable'; import { Badge, Button, Card, CardContent, CardHeader, CardTitle, ConfirmDialog, DateTimePicker, FormDialog, Input, Label, Table, TableCell, TableHead, TableRow, Tabs } from '../components/ui'; import { AccessPermissionEditor, countAccessPermissionRules } from './admin/AccessPermissionEditor'; import type { ApiKeyForm, LoadState, WorkspaceSection } from '../types'; const tabs = [ { value: 'overview', label: '个人总览', icon: }, { value: 'billing', label: '余额充值', icon: }, { value: 'apiKeys', label: 'API Key', icon: }, { value: 'tasks', label: '任务记录', icon: }, ] satisfies Array<{ value: WorkspaceSection; label: string; icon: ReactNode }>; export function WorkspacePage(props: { apiKeyForm: ApiKeyForm; apiKeySecret: string; apiKeySecretsById: Record; apiKeyPolicyModels: PlatformModel[]; data: ConsoleData; message: string; section: WorkspaceSection; state: LoadState; onBatchAccessRules: (input: GatewayAccessRuleBatchRequest) => Promise; onDeleteApiKey: (apiKeyId: string) => Promise; onApiKeyFormChange: (value: ApiKeyForm) => void; onSectionChange: (value: WorkspaceSection) => void; onSubmitApiKey: (event: FormEvent) => void | Promise; onUseApiKeyForPlayground: (apiKeyId?: string) => void; }) { return (
{props.section === 'overview' && } {props.section === 'billing' && } {props.section === 'apiKeys' && } {props.section === 'tasks' && }
); } function WorkspaceOverview(props: { data: ConsoleData }) { const owner = props.data.users[0]; return (
个人中心总览 用户组策略 [item.groupKey, item.priority, item.status, item.source])} />
); } function BillingPanel() { return (
余额 resource 0.00 local 充值
); } function ApiKeyPanel(props: { apiKeyForm: ApiKeyForm; apiKeySecret: string; apiKeySecretsById: Record; apiKeyPolicyModels: PlatformModel[]; data: ConsoleData; message: string; state: LoadState; onBatchAccessRules: (input: GatewayAccessRuleBatchRequest) => Promise; onDeleteApiKey: (apiKeyId: string) => Promise; onApiKeyFormChange: (value: ApiKeyForm) => void; onSubmitApiKey: (event: FormEvent) => void | Promise; onUseApiKeyForPlayground: (apiKeyId?: string) => void; }) { const [createOpen, setCreateOpen] = useState(false); const [policyApiKeyId, setPolicyApiKeyId] = useState(''); const [pendingDelete, setPendingDelete] = useState(null); const [localMessage, setLocalMessage] = useState(''); const selectedPolicyKey = useMemo( () => props.data.apiKeys.find((item) => item.id === policyApiKeyId), [policyApiKeyId, props.data.apiKeys], ); const permissionPlatforms = useMemo(() => platformsForPermissionTree(props.apiKeyPolicyModels), [props.apiKeyPolicyModels]); async function copyApiKey(item: GatewayApiKey) { const secret = apiKeySecretFor(item, props.apiKeySecretsById); if (!secret) return; await navigator.clipboard.writeText(secret); setLocalMessage(`已复制 ${item.name || item.keyPrefix}`); } async function submitCreate(event: FormEvent) { try { await props.onSubmitApiKey(event); setCreateOpen(false); } catch { return; } } async function confirmDeleteApiKey() { if (!pendingDelete) return; await props.onDeleteApiKey(pendingDelete.id); setPendingDelete(null); } return ( <>
API Key

按 Key 维护调用凭证、最近使用时间和平台/模型权限策略。

{(localMessage || props.message) &&

{localMessage || props.message}

} 名称 API Key 权限策略 最近使用 有效期 状态 创建时间 操作 {props.data.apiKeys.length ? props.data.apiKeys.map((item) => { const secret = apiKeySecretFor(item, props.apiKeySecretsById); const summary = countAccessPermissionRules(props.data.accessRules, 'api_key', item.id); return ( {item.name} {item.keyPrefix} {secret ? maskApiKey(secret) : item.keyPrefix} {formatDateTime(item.lastUsedAt)} {formatDateTime(item.expiresAt)} {item.status} {formatDateTime(item.createdAt)} ); }) : (
暂无 API Key 点击右上角创建调用凭证。
)}
)} open={createOpen} title="创建 API Key" onClose={() => setCreateOpen(false)} onSubmit={(event) => void submitCreate(event)} > setPolicyApiKeyId('')}>关闭} open={Boolean(selectedPolicyKey)} title={selectedPolicyKey ? `权限策略:${selectedPolicyKey.name}` : '权限策略'} onClose={() => setPolicyApiKeyId('')} onSubmit={(event) => event.preventDefault()} > setPendingDelete(null)} onConfirm={confirmDeleteApiKey} /> ); } function TaskPanel(props: { data: ConsoleData }) { const task = props.data.taskResult; const usage = task?.usage ?? {}; const tokenText = usage.totalTokens ? `${usage.totalTokens}` : '-'; const chargeText = task?.finalChargeAmount ? `${task.finalChargeAmount}` : '-'; return ( 任务记录 {task ? (
{task.status} {task.kind} {task.model}
{JSON.stringify({ result: task.result, usage: task.usage, billings: task.billings, billingSummary: task.billingSummary, metrics: task.metrics }, null, 2)}
) : (
暂无任务
)}
); } function InfoItem(props: { label: string; value: string }) { return (
{props.label} {props.value}
); } function apiKeySecretFor(item: GatewayApiKey, secretsById: Record) { return secretsById[item.id] || item.secret || ''; } function maskApiKey(secret: string) { if (secret.length <= 18) return secret; return `${secret.slice(0, 12)}...${secret.slice(-4)}`; } function permissionSummaryText(summary: ReturnType) { const allow = summary.allow.platforms + summary.allow.models; const deny = summary.deny.platforms + summary.deny.models; if (allow === 0 && deny === 0) return '未配置'; return `允许 ${allow} / 拒绝 ${deny}`; } function formatDateTime(value?: string) { if (!value) return '-'; const date = new Date(value); if (Number.isNaN(date.getTime())) return '-'; return date.toLocaleString(); } function platformsForPermissionTree(models: PlatformModel[]): IntegrationPlatform[] { const byPlatform = new Map(); for (const model of models) { if (byPlatform.has(model.platformId)) continue; const name = model.platformName || model.provider || model.platformId; byPlatform.set(model.platformId, { id: model.platformId, provider: model.provider || '', platformKey: model.platformId, name, authType: '', status: 'enabled', priority: 100, defaultPricingMode: 'inherit', defaultDiscountFactor: 1, createdAt: '', updatedAt: '', }); } return Array.from(byPlatform.values()).sort((a, b) => a.name.localeCompare(b.name)); }