'use client'; import React, { useEffect, useState } from 'react'; import Link from 'next/link'; import { CheckCircle2, XCircle, AlertCircle, RefreshCw, Server, Cpu, Radio, Key, Loader2, Settings2, ScrollText, } from 'lucide-react'; import { getStatus, restartSystem, updateProviderConfig } from '@/lib/api'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; import type { ProviderStatus, SystemStatus } from '@/types'; import { pickAppText } from '@/lib/i18n/core'; import { useAppI18n } from '@/lib/i18n/provider'; type ProviderFormState = { enabled: boolean; model: string; apiKey: string; apiBase: string; requestTimeoutSeconds: string; }; export default function StatusPage() { const { locale } = useAppI18n(); const [status, setStatus] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [restartDialogOpen, setRestartDialogOpen] = useState(false); const [restarting, setRestarting] = useState(false); const [restartError, setRestartError] = useState(null); const [selectedProvider, setSelectedProvider] = useState(null); const [providerForm, setProviderForm] = useState(() => ({ enabled: false, model: '', apiKey: '', apiBase: '', requestTimeoutSeconds: '', })); const [savingProvider, setSavingProvider] = useState(false); const [providerError, setProviderError] = useState(null); const loadStatus = async () => { setLoading(true); setError(null); try { const data = await getStatus(); setStatus(data); } catch (err: any) { setError(err.message || pickAppText(locale, '连接后端失败', 'Failed to connect to the backend')); } finally { setLoading(false); } }; useEffect(() => { loadStatus(); }, []); useEffect(() => { if (!restarting) { return; } const intervalId = window.setInterval(async () => { try { await getStatus(); window.location.reload(); } catch { // Ignore failures until the container is back. } }, 3000); return () => { window.clearInterval(intervalId); }; }, [restarting]); const handleRestart = async () => { setRestartError(null); try { await restartSystem(); setRestartDialogOpen(false); setRestarting(true); } catch (err: any) { setRestartError(err.message || pickAppText(locale, '重启失败', 'Restart failed')); } }; const openProviderDialog = (provider: ProviderStatus) => { setSelectedProvider(provider); setProviderError(null); setProviderForm({ enabled: Boolean(provider.enabled || provider.has_key), model: status?.model || '', apiKey: '', apiBase: provider.api_base || provider.default_api_base || provider.detail || '', requestTimeoutSeconds: '', }); }; const handleSaveProvider = async () => { if (!selectedProvider) return; const providerId = selectedProvider.id || selectedProvider.name; setSavingProvider(true); setProviderError(null); try { const timeout = providerForm.requestTimeoutSeconds.trim() ? Number(providerForm.requestTimeoutSeconds.trim()) : undefined; if (timeout !== undefined && (!Number.isFinite(timeout) || timeout <= 0)) { throw new Error(pickAppText(locale, '请求超时必须是正数', 'Request timeout must be a positive number')); } await updateProviderConfig(providerId, { enabled: providerForm.enabled, model: providerForm.model.trim() || undefined, api_key: providerForm.apiKey.trim() || undefined, api_base: providerForm.apiBase.trim() || undefined, request_timeout_seconds: timeout, }); await loadStatus(); setSelectedProvider(null); } catch (err: any) { setProviderError(err.message || pickAppText(locale, '保存提供商配置失败', 'Failed to save provider settings')); } finally { setSavingProvider(false); } }; if (loading) { return (
); } if (error) { return (

{pickAppText(locale, '无法连接到 Boardware Agent Sandbox 后端', 'Unable to connect to the Boardware Agent Sandbox backend')}

{error}

{pickAppText(locale, '请确认后端服务已启动,并且当前页面可以访问它。', 'Please confirm the backend service is running and reachable from this page.')}

); } if (!status) return null; const settingsLinks = [ { href: '/logs', icon: ScrollText, title: pickAppText(locale, '运行日志', 'Runtime Logs'), description: pickAppText(locale, '查看每次对话和后台任务的运行日志。', 'Inspect chat and background runtime logs.'), }, ]; return (

{pickAppText(locale, '配置', 'Settings')}

{pickAppText( locale, '集中管理模型、工具、集成和实例运行状态。Task 和通知只在各自页面处理。', 'Manage models, tools, integrations, and instance runtime status. Tasks and notifications stay in their own pages.' )}

{settingsLinks.map((item) => { const Icon = item.icon; return ( {item.title} {item.description} ); })}
{/* System Info */} {pickAppText(locale, '实例运行', 'Instance runtime')}

{pickAppText(locale, '运行与调试', 'Runtime and debugging')}

{restarting ? pickAppText(locale, '正在重启当前 docker,服务恢复后页面会自动刷新。', 'Restarting the current Docker container. The page will refresh automatically once the service is back.') : pickAppText(locale, '查看每次对话的运行日志,或重启当前 docker 容器。重启完成后需要重新登录。', 'Inspect per-chat runtime logs or restart the current Docker container. You will need to sign in again afterwards.')}

{restartError ? (

{restartError}

) : null}
{pickAppText(locale, '确认重启当前实例?', 'Restart the current instance?')} {pickAppText(locale, '这会重启当前 docker 容器,页面会短暂不可用。由于当前登录态保存在内存里,重启完成后需要重新登录。', 'This restarts the current Docker container and the page will be temporarily unavailable. Because the current sign-in state is stored in memory, you will need to sign in again after the restart.')} {pickAppText(locale, '取消', 'Cancel')} {restarting ? pickAppText(locale, '重启中...', 'Restarting...') : pickAppText(locale, '确认重启', 'Confirm restart')}
{/* Model Config */} {pickAppText(locale, '智能体配置', 'Agent configuration')} {/* Providers */} {pickAppText(locale, '提供商', 'Providers')}
{status.providers.map((p) => ( ))}
!open && setSelectedProvider(null)}> {pickAppText(locale, '配置提供商', 'Configure provider')} {selectedProvider ? ` · ${providerLabel(selectedProvider)}` : ''} {pickAppText(locale, '启用后会把它设为当前实例默认提供商。API Key 留空会保留已保存的值。', 'When enabled, this becomes the default provider for this instance. Leave API key empty to keep the saved value.')}

{pickAppText(locale, '关闭会从配置中移除这个提供商', 'Turning this off removes this provider from config')}

setProviderForm((prev) => ({ ...prev, enabled: checked }))} />
setProviderForm((prev) => ({ ...prev, model: event.target.value }))} placeholder="qwen-plus" disabled={!providerForm.enabled} />
setProviderForm((prev) => ({ ...prev, apiKey: event.target.value }))} placeholder={selectedProvider?.api_key_masked || pickAppText(locale, '留空保持不变', 'Leave blank to keep existing')} disabled={!providerForm.enabled || Boolean(selectedProvider?.is_oauth)} />
setProviderForm((prev) => ({ ...prev, apiBase: event.target.value }))} placeholder={selectedProvider?.default_api_base || 'https://api.example.com/v1'} disabled={!providerForm.enabled || Boolean(selectedProvider?.is_oauth)} />
setProviderForm((prev) => ({ ...prev, requestTimeoutSeconds: event.target.value }))} placeholder={pickAppText(locale, '默认', 'Default')} disabled={!providerForm.enabled} />
{providerError ? (

{providerError}

) : null}
{/* Channels */} {pickAppText(locale, '通道', 'Channels')}
{status.channels.map((ch) => (
{ch.enabled ? pickAppText(locale, '开启', 'On') : pickAppText(locale, '关闭', 'Off')} {ch.name}
))}
); } function InfoRow({ label, value, ok, }: { label: string; value: string; ok?: boolean; }) { return (
{label}
{value} {ok !== undefined && (ok ? ( ) : ( ))}
); } function providerLabel(provider: ProviderStatus): string { return provider.label || provider.name; }