import { KeyRound, ShieldCheck, Trash2 } from 'lucide-react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import type { ExternalApiKeyCreateResponse, ExternalApiKeyProfile, } from '../../../packages/shared/src/contracts/runtime'; import { createPlatformProfileExternalApiKey, listPlatformProfileExternalApiKeys, revokePlatformProfileExternalApiKey, } from '../../services/platform-entry/platformProfileClient'; import { CopyFeedbackButton } from '../common/CopyFeedbackButton'; import { PlatformActionButton } from '../common/PlatformActionButton'; import { PlatformAsyncStatePanel } from '../common/PlatformAsyncStatePanel'; import { PlatformEmptyState } from '../common/PlatformEmptyState'; import { PlatformProfileContentRow } from '../common/PlatformProfileContentRow'; import { PlatformProfileSkeletonList } from '../common/PlatformProfileSkeletonList'; import { PlatformProfileSummaryHeader } from '../common/PlatformProfileSummaryHeader'; import { PlatformStatusMessage } from '../common/PlatformStatusMessage'; import { PlatformTextField } from '../common/PlatformTextField'; import { useCopyFeedback } from '../common/useCopyFeedback'; import { formatPlatformProfileTime } from './platformProfileFundsModel'; import { PlatformProfileSecondaryModalShell } from './PlatformProfileModalShell'; type PlatformProfileApiKeysModalProps = { onClose: () => void; }; function isActiveExternalApiKey(key: ExternalApiKeyProfile) { return !key.revokedAt; } function buildApiKeyTimeLabel(value: string | null) { if (!value) { return '尚未使用'; } return formatPlatformProfileTime(value); } function buildApiKeyScopeLabel(scopes: string[]) { return scopes.length > 0 ? scopes.join(' / ') : '默认权限'; } /** * 开发者 API Key 管理弹窗。 * 明文 Key 只保留在本次创建后的组件状态里,关闭弹窗后即丢弃。 */ export function PlatformProfileApiKeysModal({ onClose, }: PlatformProfileApiKeysModalProps) { const [keys, setKeys] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [nameInput, setNameInput] = useState('外部 API Key'); const [createdKey, setCreatedKey] = useState(null); const [isCreating, setIsCreating] = useState(false); const [revokingKeyId, setRevokingKeyId] = useState(null); const { copyState, copyText } = useCopyFeedback(); const activeKeys = useMemo(() => keys.filter(isActiveExternalApiKey), [keys]); const shouldShowBlockingError = Boolean( error && !isLoading && keys.length === 0, ); const loadKeys = useCallback(() => { setIsLoading(true); setError(null); void listPlatformProfileExternalApiKeys() .then((response) => { setKeys(response.keys); }) .catch((loadError: unknown) => { setError( loadError instanceof Error ? loadError.message : '读取 API Key 失败', ); }) .finally(() => setIsLoading(false)); }, []); useEffect(() => { loadKeys(); }, [loadKeys]); const createKey = useCallback(() => { if (isCreating) { return; } setIsCreating(true); setError(null); setCreatedKey(null); void createPlatformProfileExternalApiKey(nameInput) .then((response) => { setCreatedKey(response); setKeys((current) => [ response.key, ...current.filter((key) => key.keyId !== response.key.keyId), ]); setNameInput('外部 API Key'); }) .catch((createError: unknown) => { setError( createError instanceof Error ? createError.message : '创建 API Key 失败', ); }) .finally(() => setIsCreating(false)); }, [isCreating, nameInput]); const revokeKey = useCallback((keyId: string) => { setRevokingKeyId(keyId); setError(null); void revokePlatformProfileExternalApiKey(keyId) .then((response) => { setKeys((current) => current.map((key) => key.keyId === response.key.keyId ? response.key : key, ), ); setCreatedKey((current) => current?.key.keyId === response.key.keyId ? null : current, ); }) .catch((revokeError: unknown) => { setError( revokeError instanceof Error ? revokeError.message : '撤销 API Key 失败', ); }) .finally(() => setRevokingKeyId(null)); }, []); return ( {activeKeys.length} 个可用 } />
{isCreating ? '创建中' : '创建'}
{createdKey ? (
新 Key
{createdKey.apiKey}
void copyText(createdKey.apiKey)} />
) : null}
{error}
重新加载 ) : null } isLoading={isLoading} loadingState={ } isEmpty={keys.length === 0} emptyState={ 暂无 API Key } > {error ? ( {error} ) : null}
{keys.map((key) => { const isActive = isActiveExternalApiKey(key); const isRevoking = revokingKeyId === key.keyId; return (
{key.name} {isActive ? '可用' : '已撤销'}
{key.keyPrefix}...
{buildApiKeyScopeLabel(key.scopes)}
创建 {buildApiKeyTimeLabel(key.createdAt)} · 最近使用{' '} {buildApiKeyTimeLabel(key.lastUsedAt)}
{isActive ? ( revokeKey(key.keyId)} > {isRevoking ? '撤销中' : '撤销'} ) : null}
); })}
); }