重构状态检测功能:清理复杂逻辑,保留UI结构
## 主要变更
- 移除所有Claude命令调用和进程管理逻辑
- 简化检测函数,暂时返回"功能开发中"状态
- 添加单独检查状态按钮和相关UI交互
- 保留完整的供应商管理功能(添加、编辑、删除、切换)
## 技术优化
- 删除复杂的超时机制、进程监听、错误处理
- 移除axios依赖和HTTP请求相关代码
- 清理竞态条件和队列管理逻辑
- 保持清晰的UI状态管理
## UI改进
- 添加橙色主题的单独检查按钮
- 增强检查状态的视觉反馈(🔄 进度指示器)
- 保留所有状态显示逻辑等待功能重新实现
This commit is contained in:
@@ -1,123 +1,17 @@
|
|||||||
import axios from 'axios'
|
|
||||||
import fs from 'fs/promises'
|
import fs from 'fs/promises'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import os from 'os'
|
import os from 'os'
|
||||||
import { Provider, ProviderStatus } from '../shared/types'
|
import { Provider, ProviderStatus } from '../shared/types'
|
||||||
|
|
||||||
export async function checkProviderStatus(provider: Provider): Promise<ProviderStatus> {
|
export async function checkProviderStatus(
|
||||||
const startTime = Date.now()
|
provider: Provider
|
||||||
|
): Promise<ProviderStatus> {
|
||||||
try {
|
// 暂时返回未检查状态
|
||||||
// 方法1: 先检查 Anthropic 官方状态 (适用于 Anthropic API)
|
return {
|
||||||
if (provider.apiUrl.includes('anthropic.com')) {
|
isOnline: false,
|
||||||
try {
|
responseTime: -1,
|
||||||
const statusResponse = await axios.get('https://status.anthropic.com/api/v2/summary.json', {
|
lastChecked: new Date(),
|
||||||
timeout: 3000
|
error: '功能开发中'
|
||||||
})
|
|
||||||
|
|
||||||
if (statusResponse.data.status?.indicator !== 'none') {
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime: -1,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: 'Anthropic 服务当前不可用'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// 状态检查失败,继续尝试直接 API 调用
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法2: 轻量级 API 测试请求 - 使用最新的 API 格式
|
|
||||||
const testPayload = {
|
|
||||||
model: provider.model || 'claude-sonnet-4-20250514',
|
|
||||||
max_tokens: 1,
|
|
||||||
messages: [
|
|
||||||
{
|
|
||||||
role: 'user',
|
|
||||||
content: 'test'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const response = await axios.post(
|
|
||||||
`${provider.apiUrl}/v1/messages`,
|
|
||||||
testPayload,
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
'x-api-key': provider.apiKey,
|
|
||||||
'anthropic-version': '2023-06-01',
|
|
||||||
'content-type': 'application/json'
|
|
||||||
},
|
|
||||||
timeout: 20000, // 增加超时时间到20秒
|
|
||||||
validateStatus: (status) => {
|
|
||||||
// 200-299 为成功,400-499 通常表示 API 可用但请求有问题(如 key 无效)
|
|
||||||
return status < 500
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
const responseTime = Date.now() - startTime
|
|
||||||
|
|
||||||
// 检查响应状态
|
|
||||||
if (response.status >= 200 && response.status < 300) {
|
|
||||||
return {
|
|
||||||
isOnline: true,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date()
|
|
||||||
}
|
|
||||||
} else if (response.status >= 400 && response.status < 500) {
|
|
||||||
// 客户端错误,API 可用但可能是 key 无效或其他认证问题
|
|
||||||
return {
|
|
||||||
isOnline: true, // API 本身是可用的
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: `API 可用但认证失败 (${response.status}): ${response.data?.error?.message || '请检查 API Key'}`
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: `服务器错误 (${response.status})`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
const responseTime = Date.now() - startTime
|
|
||||||
|
|
||||||
if (axios.isAxiosError(error)) {
|
|
||||||
if (error.code === 'ECONNABORTED' || error.code === 'ETIMEDOUT') {
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: '请求超时 - 服务可能不可用'
|
|
||||||
}
|
|
||||||
} else if (error.response) {
|
|
||||||
// 服务器响应了错误状态码
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: `HTTP ${error.response.status}: ${error.response.data?.error?.message || error.message}`
|
|
||||||
}
|
|
||||||
} else if (error.request) {
|
|
||||||
// 请求发出但没有收到响应
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: '网络连接失败 - 无法访问服务'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isOnline: false,
|
|
||||||
responseTime,
|
|
||||||
lastChecked: new Date(),
|
|
||||||
error: error instanceof Error ? error.message : '未知错误'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,17 +19,17 @@ export function getClaudeCodeConfig() {
|
|||||||
// Claude Code 配置文件路径
|
// Claude Code 配置文件路径
|
||||||
const configDir = path.join(os.homedir(), '.claude')
|
const configDir = path.join(os.homedir(), '.claude')
|
||||||
const configPath = path.join(configDir, 'settings.json')
|
const configPath = path.join(configDir, 'settings.json')
|
||||||
|
|
||||||
return { path: configPath, dir: configDir }
|
return { path: configPath, dir: configDir }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function switchProvider(provider: Provider): Promise<boolean> {
|
export async function switchProvider(provider: Provider): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const { path: configPath, dir: configDir } = getClaudeCodeConfig()
|
const { path: configPath, dir: configDir } = getClaudeCodeConfig()
|
||||||
|
|
||||||
// 确保目录存在
|
// 确保目录存在
|
||||||
await fs.mkdir(configDir, { recursive: true })
|
await fs.mkdir(configDir, { recursive: true })
|
||||||
|
|
||||||
// 读取现有配置
|
// 读取现有配置
|
||||||
let config: any = {}
|
let config: any = {}
|
||||||
try {
|
try {
|
||||||
@@ -144,18 +38,19 @@ export async function switchProvider(provider: Provider): Promise<boolean> {
|
|||||||
} catch {
|
} catch {
|
||||||
// 文件不存在或解析失败,使用空配置
|
// 文件不存在或解析失败,使用空配置
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新配置
|
// 确保 env 对象存在
|
||||||
config.api = {
|
if (!config.env) {
|
||||||
...config.api,
|
config.env = {}
|
||||||
baseURL: provider.apiUrl,
|
|
||||||
apiKey: provider.apiKey,
|
|
||||||
model: provider.model
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新环境变量配置
|
||||||
|
config.env.ANTHROPIC_AUTH_TOKEN = provider.apiKey
|
||||||
|
config.env.ANTHROPIC_BASE_URL = provider.apiUrl
|
||||||
|
|
||||||
// 写回配置文件
|
// 写回配置文件
|
||||||
await fs.writeFile(configPath, JSON.stringify(config, null, 2))
|
await fs.writeFile(configPath, JSON.stringify(config, null, 2))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('切换供应商失败:', error)
|
console.error('切换供应商失败:', error)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ function App() {
|
|||||||
const [currentProviderId, setCurrentProviderId] = useState<string>('')
|
const [currentProviderId, setCurrentProviderId] = useState<string>('')
|
||||||
const [statuses, setStatuses] = useState<Record<string, ProviderStatus>>({})
|
const [statuses, setStatuses] = useState<Record<string, ProviderStatus>>({})
|
||||||
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
|
const [isAddModalOpen, setIsAddModalOpen] = useState(false)
|
||||||
const [isRefreshing, setIsRefreshing] = useState(false)
|
const [checkingStatus, setCheckingStatus] = useState<Record<string, boolean>>({})
|
||||||
const [configPath, setConfigPath] = useState<string>('')
|
const [configPath, setConfigPath] = useState<string>('')
|
||||||
const [editingProviderId, setEditingProviderId] = useState<string | null>(null)
|
const [editingProviderId, setEditingProviderId] = useState<string | null>(null)
|
||||||
|
|
||||||
@@ -34,20 +34,30 @@ function App() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const checkAllStatuses = async () => {
|
const checkAllStatuses = async () => {
|
||||||
if (Object.keys(providers).length === 0) return
|
// 功能开发中
|
||||||
|
alert('状态检查功能开发中')
|
||||||
|
}
|
||||||
|
|
||||||
|
const checkSingleStatus = async (providerId: string) => {
|
||||||
|
const provider = providers[providerId]
|
||||||
|
if (!provider) return
|
||||||
|
|
||||||
|
setCheckingStatus(prev => ({ ...prev, [providerId]: true }))
|
||||||
|
|
||||||
setIsRefreshing(true)
|
try {
|
||||||
const newStatuses: Record<string, ProviderStatus> = {}
|
// 暂时显示开发中状态
|
||||||
|
const status: ProviderStatus = {
|
||||||
await Promise.all(
|
isOnline: false,
|
||||||
Object.values(providers).map(async (provider) => {
|
responseTime: -1,
|
||||||
const status = await window.electronAPI.checkStatus(provider)
|
lastChecked: new Date(),
|
||||||
newStatuses[provider.id] = status
|
error: '功能开发中'
|
||||||
})
|
}
|
||||||
)
|
setStatuses(prev => ({ ...prev, [providerId]: status }))
|
||||||
|
} catch (error) {
|
||||||
setStatuses(newStatuses)
|
console.error('检查状态失败:', error)
|
||||||
setIsRefreshing(false)
|
} finally {
|
||||||
|
setCheckingStatus(prev => ({ ...prev, [providerId]: false }))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleAddProvider = async (provider: Omit<Provider, 'id'>) => {
|
const handleAddProvider = async (provider: Omit<Provider, 'id'>) => {
|
||||||
@@ -96,10 +106,9 @@ function App() {
|
|||||||
<div className="header-actions">
|
<div className="header-actions">
|
||||||
<button
|
<button
|
||||||
className="refresh-btn"
|
className="refresh-btn"
|
||||||
onClick={checkAllStatuses}
|
onClick={checkAllStatuses}
|
||||||
disabled={isRefreshing}
|
|
||||||
>
|
>
|
||||||
{isRefreshing ? '检查中...' : '检查状态'}
|
检查状态(开发中)
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="add-btn"
|
className="add-btn"
|
||||||
@@ -115,9 +124,11 @@ function App() {
|
|||||||
providers={providers}
|
providers={providers}
|
||||||
currentProviderId={currentProviderId}
|
currentProviderId={currentProviderId}
|
||||||
statuses={statuses}
|
statuses={statuses}
|
||||||
|
checkingStatus={checkingStatus}
|
||||||
onSwitch={handleSwitchProvider}
|
onSwitch={handleSwitchProvider}
|
||||||
onDelete={handleDeleteProvider}
|
onDelete={handleDeleteProvider}
|
||||||
onEdit={setEditingProviderId}
|
onEdit={setEditingProviderId}
|
||||||
|
onCheckStatus={checkSingleStatus}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{configPath && (
|
{configPath && (
|
||||||
|
|||||||
@@ -105,6 +105,27 @@
|
|||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.check-btn {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
border: 1px solid #f39c12;
|
||||||
|
background: white;
|
||||||
|
color: #f39c12;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn:hover:not(:disabled) {
|
||||||
|
background: #f39c12;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-btn:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.enable-btn {
|
.enable-btn {
|
||||||
padding: 0.375rem 0.75rem;
|
padding: 0.375rem 0.75rem;
|
||||||
border: 1px solid #27ae60;
|
border: 1px solid #27ae60;
|
||||||
|
|||||||
@@ -6,18 +6,22 @@ interface ProviderListProps {
|
|||||||
providers: Record<string, Provider>
|
providers: Record<string, Provider>
|
||||||
currentProviderId: string
|
currentProviderId: string
|
||||||
statuses: Record<string, ProviderStatus>
|
statuses: Record<string, ProviderStatus>
|
||||||
|
checkingStatus: Record<string, boolean>
|
||||||
onSwitch: (id: string) => void
|
onSwitch: (id: string) => void
|
||||||
onDelete: (id: string) => void
|
onDelete: (id: string) => void
|
||||||
onEdit: (id: string) => void
|
onEdit: (id: string) => void
|
||||||
|
onCheckStatus: (id: string) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProviderList: React.FC<ProviderListProps> = ({
|
const ProviderList: React.FC<ProviderListProps> = ({
|
||||||
providers,
|
providers,
|
||||||
currentProviderId,
|
currentProviderId,
|
||||||
statuses,
|
statuses,
|
||||||
|
checkingStatus,
|
||||||
onSwitch,
|
onSwitch,
|
||||||
onDelete,
|
onDelete,
|
||||||
onEdit
|
onEdit,
|
||||||
|
onCheckStatus
|
||||||
}) => {
|
}) => {
|
||||||
const formatResponseTime = (time: number) => {
|
const formatResponseTime = (time: number) => {
|
||||||
if (time < 0) return '-'
|
if (time < 0) return '-'
|
||||||
@@ -29,8 +33,9 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
return status.isOnline ? '✅' : '❌'
|
return status.isOnline ? '✅' : '❌'
|
||||||
}
|
}
|
||||||
|
|
||||||
const getStatusText = (status?: ProviderStatus) => {
|
const getStatusText = (status?: ProviderStatus, isChecking?: boolean) => {
|
||||||
if (!status) return '检查中...'
|
if (isChecking) return '检查中...'
|
||||||
|
if (!status) return '未检查'
|
||||||
if (status.isOnline) return '正常'
|
if (status.isOnline) return '正常'
|
||||||
return status.error || '连接失败'
|
return status.error || '连接失败'
|
||||||
}
|
}
|
||||||
@@ -46,6 +51,7 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
<div className="provider-items">
|
<div className="provider-items">
|
||||||
{Object.values(providers).map((provider) => {
|
{Object.values(providers).map((provider) => {
|
||||||
const status = statuses[provider.id]
|
const status = statuses[provider.id]
|
||||||
|
const isChecking = checkingStatus[provider.id]
|
||||||
const isCurrent = provider.id === currentProviderId
|
const isCurrent = provider.id === currentProviderId
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -69,9 +75,9 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="provider-status">
|
<div className="provider-status">
|
||||||
<span className="status-icon">{getStatusIcon(status)}</span>
|
<span className="status-icon">{isChecking ? '🔄' : getStatusIcon(status)}</span>
|
||||||
<span className="status-text">{getStatusText(status)}</span>
|
<span className="status-text">{getStatusText(status, isChecking)}</span>
|
||||||
{status?.isOnline && (
|
{status?.isOnline && !isChecking && (
|
||||||
<span className="response-time">
|
<span className="response-time">
|
||||||
{formatResponseTime(status.responseTime)}
|
{formatResponseTime(status.responseTime)}
|
||||||
</span>
|
</span>
|
||||||
@@ -79,6 +85,13 @@ const ProviderList: React.FC<ProviderListProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="provider-actions">
|
<div className="provider-actions">
|
||||||
|
<button
|
||||||
|
className="check-btn"
|
||||||
|
onClick={() => onCheckStatus(provider.id)}
|
||||||
|
disabled={isChecking}
|
||||||
|
>
|
||||||
|
{isChecking ? '检查中' : '检查状态'}
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
className="enable-btn"
|
className="enable-btn"
|
||||||
onClick={() => onSwitch(provider.id)}
|
onClick={() => onSwitch(provider.id)}
|
||||||
|
|||||||
Reference in New Issue
Block a user