'use client'; // ApiKeysTab — API Key 录入(只录不显余额,CLAUDE.md红线) import { useEffect, useState } from 'react'; import { api } from '@/lib/api'; import { ApiKey, CreateApiKeyRequest, PaginatedResponse, ApiError } from '@/types/index'; import { getErrorAction } from '@/types/errors'; export function ApiKeysTab() { const [keys, setKeys] = useState([]); const [form, setForm] = useState({ provider: 'openai', api_key: '' }); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [saving, setSaving] = useState(false); useEffect(() => { loadKeys(); }, []); async function loadKeys() { setLoading(true); try { const data = await api.get>('/api/v1/api-keys'); setKeys(data.items); } catch { /* silent */ } finally { setLoading(false); } } async function handleAdd(e: React.FormEvent) { e.preventDefault(); if (!form.api_key.trim()) { setError('请填写 API Key'); return; } setSaving(true); setError(''); try { await api.post('/api/v1/api-keys', form); setForm({ provider: 'openai', api_key: '' }); await loadKeys(); } catch (err) { const apiErr = err as ApiError; setError(apiErr?.message || getErrorAction(apiErr?.code).message); } finally { setSaving(false); } } async function handleDelete(id: number) { if (!confirm('确认删除此 Key?')) return; try { await api.delete(`/api/v1/api-keys/${id}`); setKeys((ks) => ks.filter((k) => k.id !== id)); } catch { /* ignore */ } } return (

API Key 由各人自录,余额和用量请到中转站后台查看。

setForm((f) => ({ ...f, api_key: e.target.value }))} placeholder="sk-..." className="w-full border border-border-default rounded-lg px-3 py-2 text-sm focus:outline-none focus:border-brand-orange" aria-label="输入 API Key" />
{error &&

{error}

} {loading ? (
加载中…
) : (
    {keys.map((k) => (
  • {k.provider} …{k.key_last4}
  • ))} {keys.length === 0 && (
  • 暂无 Key,请先录入
  • )}
)}
); }