mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 03:55:55 +00:00
feat(web): show Box runtime status in plugin debug info popover
Add Box status section to the debug info popover on the plugin list page, displaying connection status, backend info, profile, active sessions, and recent error count. Fetched from GET /api/v1/box/status in parallel with plugin debug info. Includes i18n for all 8 supported languages.
This commit is contained in:
@@ -17,6 +17,10 @@ import {
|
|||||||
Check,
|
Check,
|
||||||
Bug,
|
Bug,
|
||||||
Unlink,
|
Unlink,
|
||||||
|
Box,
|
||||||
|
CircleCheck,
|
||||||
|
CircleX,
|
||||||
|
Activity,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -43,7 +47,10 @@ import { httpClient } from '@/app/infra/http/HttpClient';
|
|||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { systemInfo } from '@/app/infra/http/HttpClient';
|
import { systemInfo } from '@/app/infra/http/HttpClient';
|
||||||
import { ApiRespPluginSystemStatus } from '@/app/infra/entities/api';
|
import {
|
||||||
|
ApiRespPluginSystemStatus,
|
||||||
|
ApiRespBoxStatus,
|
||||||
|
} from '@/app/infra/entities/api';
|
||||||
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
|
import { useSidebarData } from '@/app/home/components/home-sidebar/SidebarDataContext';
|
||||||
import {
|
import {
|
||||||
PluginInstallTaskQueue,
|
PluginInstallTaskQueue,
|
||||||
@@ -133,6 +140,7 @@ function PluginListView() {
|
|||||||
const [debugPopoverOpen, setDebugPopoverOpen] = useState(false);
|
const [debugPopoverOpen, setDebugPopoverOpen] = useState(false);
|
||||||
const [copiedDebugUrl, setCopiedDebugUrl] = useState(false);
|
const [copiedDebugUrl, setCopiedDebugUrl] = useState(false);
|
||||||
const [copiedDebugKey, setCopiedDebugKey] = useState(false);
|
const [copiedDebugKey, setCopiedDebugKey] = useState(false);
|
||||||
|
const [boxStatus, setBoxStatus] = useState<ApiRespBoxStatus | null>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchPluginSystemStatus = async () => {
|
const fetchPluginSystemStatus = async () => {
|
||||||
@@ -454,8 +462,12 @@ function PluginListView() {
|
|||||||
|
|
||||||
const handleShowDebugInfo = async () => {
|
const handleShowDebugInfo = async () => {
|
||||||
try {
|
try {
|
||||||
const info = await httpClient.getPluginDebugInfo();
|
const [info, box] = await Promise.all([
|
||||||
|
httpClient.getPluginDebugInfo(),
|
||||||
|
httpClient.getBoxStatus().catch(() => null),
|
||||||
|
]);
|
||||||
setDebugInfo(info);
|
setDebugInfo(info);
|
||||||
|
setBoxStatus(box);
|
||||||
setDebugPopoverOpen(true);
|
setDebugPopoverOpen(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch debug info:', error);
|
console.error('Failed to fetch debug info:', error);
|
||||||
@@ -645,6 +657,82 @@ function PluginListView() {
|
|||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Box Runtime Status */}
|
||||||
|
<div className="pt-2 border-t">
|
||||||
|
<div className="flex items-center gap-2 pb-2">
|
||||||
|
<Box className="w-4 h-4" />
|
||||||
|
<h4 className="font-semibold text-sm">
|
||||||
|
{t('plugins.boxStatusTitle')}
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
{boxStatus ? (
|
||||||
|
<div className="space-y-1.5 text-xs">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground min-w-[80px]">
|
||||||
|
{t('plugins.boxStatus')}:
|
||||||
|
</span>
|
||||||
|
{boxStatus.available ? (
|
||||||
|
<span className="flex items-center gap-1 text-green-600">
|
||||||
|
<CircleCheck className="w-3.5 h-3.5" />
|
||||||
|
{t('plugins.boxConnected')}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
<span className="flex items-center gap-1 text-red-500">
|
||||||
|
<CircleX className="w-3.5 h-3.5" />
|
||||||
|
{t('plugins.boxUnavailable')}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{boxStatus.backend && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground min-w-[80px]">
|
||||||
|
{t('plugins.boxBackend')}:
|
||||||
|
</span>
|
||||||
|
<span className="font-mono">
|
||||||
|
{boxStatus.backend.name}
|
||||||
|
{boxStatus.backend.available
|
||||||
|
? ''
|
||||||
|
: ` (${t('plugins.boxUnavailable')})`}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground min-w-[80px]">
|
||||||
|
{t('plugins.boxProfile')}:
|
||||||
|
</span>
|
||||||
|
<span className="font-mono">{boxStatus.profile}</span>
|
||||||
|
</div>
|
||||||
|
{boxStatus.available && (
|
||||||
|
<>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground min-w-[80px]">
|
||||||
|
{t('plugins.boxSessions')}:
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1 font-mono">
|
||||||
|
<Activity className="w-3 h-3" />
|
||||||
|
{boxStatus.active_sessions ?? 0}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
{(boxStatus.recent_error_count ?? 0) > 0 && (
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="text-muted-foreground min-w-[80px]">
|
||||||
|
{t('plugins.boxErrors')}:
|
||||||
|
</span>
|
||||||
|
<span className="text-amber-600 font-mono">
|
||||||
|
{boxStatus.recent_error_count}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<p className="text-xs text-muted-foreground">
|
||||||
|
{t('plugins.boxStatusLoadFailed')}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</PopoverContent>
|
</PopoverContent>
|
||||||
</Popover>
|
</Popover>
|
||||||
|
|||||||
@@ -351,6 +351,19 @@ export interface ApiRespPluginSystemStatus {
|
|||||||
plugin_connector_error: string;
|
plugin_connector_error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ApiRespBoxStatus {
|
||||||
|
available: boolean;
|
||||||
|
profile: string;
|
||||||
|
recent_error_count: number;
|
||||||
|
backend?: {
|
||||||
|
name: string;
|
||||||
|
available: boolean;
|
||||||
|
};
|
||||||
|
active_sessions?: number;
|
||||||
|
managed_processes?: number;
|
||||||
|
session_ttl_sec?: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface ApiRespAsyncTasks {
|
export interface ApiRespAsyncTasks {
|
||||||
tasks: AsyncTask[];
|
tasks: AsyncTask[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import {
|
|||||||
ApiRespProviderRerankModel,
|
ApiRespProviderRerankModel,
|
||||||
RerankModel,
|
RerankModel,
|
||||||
ApiRespPluginSystemStatus,
|
ApiRespPluginSystemStatus,
|
||||||
|
ApiRespBoxStatus,
|
||||||
ApiRespMCPServers,
|
ApiRespMCPServers,
|
||||||
ApiRespMCPServer,
|
ApiRespMCPServer,
|
||||||
MCPServer,
|
MCPServer,
|
||||||
@@ -889,6 +890,10 @@ export class BackendClient extends BaseHttpClient {
|
|||||||
return this.get('/api/v1/plugins/debug-info');
|
return this.get('/api/v1/plugins/debug-info');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getBoxStatus(): Promise<ApiRespBoxStatus> {
|
||||||
|
return this.get('/api/v1/box/status');
|
||||||
|
}
|
||||||
|
|
||||||
// ============ User API ============
|
// ============ User API ============
|
||||||
public checkIfInited(): Promise<{ initialized: boolean }> {
|
public checkIfInited(): Promise<{ initialized: boolean }> {
|
||||||
return this.get('/api/v1/user/init');
|
return this.get('/api/v1/user/init');
|
||||||
|
|||||||
@@ -467,6 +467,15 @@ const enUS = {
|
|||||||
noDebugKey: '(Not Set)',
|
noDebugKey: '(Not Set)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'Debug key is not set, plugin debugging does not require authentication',
|
'Debug key is not set, plugin debugging does not require authentication',
|
||||||
|
boxStatusTitle: 'Box Runtime',
|
||||||
|
boxStatus: 'Status',
|
||||||
|
boxConnected: 'Connected',
|
||||||
|
boxUnavailable: 'Unavailable',
|
||||||
|
boxBackend: 'Backend',
|
||||||
|
boxProfile: 'Profile',
|
||||||
|
boxSessions: 'Sessions',
|
||||||
|
boxErrors: 'Errors',
|
||||||
|
boxStatusLoadFailed: 'Failed to load Box status',
|
||||||
failedToGetDebugInfo: 'Failed to get debug information',
|
failedToGetDebugInfo: 'Failed to get debug information',
|
||||||
copiedToClipboard: 'Copied to clipboard',
|
copiedToClipboard: 'Copied to clipboard',
|
||||||
deleting: 'Deleting...',
|
deleting: 'Deleting...',
|
||||||
|
|||||||
@@ -479,6 +479,15 @@ const esES = {
|
|||||||
noDebugKey: '(No establecida)',
|
noDebugKey: '(No establecida)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'La clave de depuración no está configurada, la depuración del plugin no requiere autenticación',
|
'La clave de depuración no está configurada, la depuración del plugin no requiere autenticación',
|
||||||
|
boxStatusTitle: 'Box Runtime',
|
||||||
|
boxStatus: 'Estado',
|
||||||
|
boxConnected: 'Conectado',
|
||||||
|
boxUnavailable: 'No disponible',
|
||||||
|
boxBackend: 'Backend',
|
||||||
|
boxProfile: 'Perfil',
|
||||||
|
boxSessions: 'Sesiones',
|
||||||
|
boxErrors: 'Errores',
|
||||||
|
boxStatusLoadFailed: 'Error al cargar el estado de Box',
|
||||||
failedToGetDebugInfo: 'Error al obtener la información de depuración',
|
failedToGetDebugInfo: 'Error al obtener la información de depuración',
|
||||||
copiedToClipboard: 'Copiado al portapapeles',
|
copiedToClipboard: 'Copiado al portapapeles',
|
||||||
deleting: 'Eliminando...',
|
deleting: 'Eliminando...',
|
||||||
|
|||||||
@@ -472,6 +472,15 @@ const jaJP = {
|
|||||||
noDebugKey: '(未設定)',
|
noDebugKey: '(未設定)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'デバッグキーが設定されていません。プラグインデバッグには認証が不要です',
|
'デバッグキーが設定されていません。プラグインデバッグには認証が不要です',
|
||||||
|
boxStatusTitle: 'Box ランタイム',
|
||||||
|
boxStatus: 'ステータス',
|
||||||
|
boxConnected: '接続済み',
|
||||||
|
boxUnavailable: '利用不可',
|
||||||
|
boxBackend: 'バックエンド',
|
||||||
|
boxProfile: 'プロファイル',
|
||||||
|
boxSessions: 'セッション',
|
||||||
|
boxErrors: 'エラー',
|
||||||
|
boxStatusLoadFailed: 'Box ステータスの読み込みに失敗しました',
|
||||||
failedToGetDebugInfo: 'デバッグ情報の取得に失敗しました',
|
failedToGetDebugInfo: 'デバッグ情報の取得に失敗しました',
|
||||||
copiedToClipboard: 'クリップボードにコピーしました',
|
copiedToClipboard: 'クリップボードにコピーしました',
|
||||||
deleting: '削除中...',
|
deleting: '削除中...',
|
||||||
|
|||||||
@@ -475,6 +475,15 @@ const ruRU = {
|
|||||||
noDebugKey: '(Не задан)',
|
noDebugKey: '(Не задан)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'Ключ отладки не задан, аутентификация при отладке плагина не требуется',
|
'Ключ отладки не задан, аутентификация при отладке плагина не требуется',
|
||||||
|
boxStatusTitle: 'Box Runtime',
|
||||||
|
boxStatus: 'Статус',
|
||||||
|
boxConnected: 'Подключено',
|
||||||
|
boxUnavailable: 'Недоступно',
|
||||||
|
boxBackend: 'Бэкенд',
|
||||||
|
boxProfile: 'Профиль',
|
||||||
|
boxSessions: 'Сессии',
|
||||||
|
boxErrors: 'Ошибки',
|
||||||
|
boxStatusLoadFailed: 'Не удалось загрузить статус Box',
|
||||||
failedToGetDebugInfo: 'Не удалось получить отладочную информацию',
|
failedToGetDebugInfo: 'Не удалось получить отладочную информацию',
|
||||||
copiedToClipboard: 'Скопировано в буфер обмена',
|
copiedToClipboard: 'Скопировано в буфер обмена',
|
||||||
deleting: 'Удаление...',
|
deleting: 'Удаление...',
|
||||||
|
|||||||
@@ -462,6 +462,15 @@ const thTH = {
|
|||||||
noDebugKey: '(ไม่ได้ตั้งค่า)',
|
noDebugKey: '(ไม่ได้ตั้งค่า)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'ไม่ได้ตั้งค่าคีย์ดีบัก การดีบักปลั๊กอินไม่ต้องยืนยันตัวตน',
|
'ไม่ได้ตั้งค่าคีย์ดีบัก การดีบักปลั๊กอินไม่ต้องยืนยันตัวตน',
|
||||||
|
boxStatusTitle: 'Box Runtime',
|
||||||
|
boxStatus: 'สถานะ',
|
||||||
|
boxConnected: 'เชื่อมต่อแล้ว',
|
||||||
|
boxUnavailable: 'ไม่พร้อมใช้งาน',
|
||||||
|
boxBackend: 'แบ็กเอนด์',
|
||||||
|
boxProfile: 'โปรไฟล์',
|
||||||
|
boxSessions: 'เซสชัน',
|
||||||
|
boxErrors: 'ข้อผิดพลาด',
|
||||||
|
boxStatusLoadFailed: 'โหลดสถานะ Box ล้มเหลว',
|
||||||
failedToGetDebugInfo: 'ไม่สามารถดึงข้อมูลดีบักได้',
|
failedToGetDebugInfo: 'ไม่สามารถดึงข้อมูลดีบักได้',
|
||||||
copiedToClipboard: 'คัดลอกไปยังคลิปบอร์ดแล้ว',
|
copiedToClipboard: 'คัดลอกไปยังคลิปบอร์ดแล้ว',
|
||||||
deleting: 'กำลังลบ...',
|
deleting: 'กำลังลบ...',
|
||||||
|
|||||||
@@ -472,6 +472,15 @@ const viVN = {
|
|||||||
noDebugKey: '(Chưa đặt)',
|
noDebugKey: '(Chưa đặt)',
|
||||||
debugKeyDisabled:
|
debugKeyDisabled:
|
||||||
'Khóa gỡ lỗi chưa được đặt, gỡ lỗi plugin không yêu cầu xác thực',
|
'Khóa gỡ lỗi chưa được đặt, gỡ lỗi plugin không yêu cầu xác thực',
|
||||||
|
boxStatusTitle: 'Box Runtime',
|
||||||
|
boxStatus: 'Trạng thái',
|
||||||
|
boxConnected: 'Đã kết nối',
|
||||||
|
boxUnavailable: 'Không khả dụng',
|
||||||
|
boxBackend: 'Backend',
|
||||||
|
boxProfile: 'Hồ sơ',
|
||||||
|
boxSessions: 'Phiên',
|
||||||
|
boxErrors: 'Lỗi',
|
||||||
|
boxStatusLoadFailed: 'Tải trạng thái Box thất bại',
|
||||||
failedToGetDebugInfo: 'Lấy thông tin gỡ lỗi thất bại',
|
failedToGetDebugInfo: 'Lấy thông tin gỡ lỗi thất bại',
|
||||||
copiedToClipboard: 'Đã sao chép vào clipboard',
|
copiedToClipboard: 'Đã sao chép vào clipboard',
|
||||||
deleting: 'Đang xóa...',
|
deleting: 'Đang xóa...',
|
||||||
|
|||||||
@@ -446,6 +446,15 @@ const zhHans = {
|
|||||||
debugKey: '调试密钥',
|
debugKey: '调试密钥',
|
||||||
noDebugKey: '(未设置)',
|
noDebugKey: '(未设置)',
|
||||||
debugKeyDisabled: '未设置调试密钥,插件调试无需认证',
|
debugKeyDisabled: '未设置调试密钥,插件调试无需认证',
|
||||||
|
boxStatusTitle: 'Box 运行时',
|
||||||
|
boxStatus: '状态',
|
||||||
|
boxConnected: '已连接',
|
||||||
|
boxUnavailable: '不可用',
|
||||||
|
boxBackend: '后端',
|
||||||
|
boxProfile: '配置',
|
||||||
|
boxSessions: '会话数',
|
||||||
|
boxErrors: '错误数',
|
||||||
|
boxStatusLoadFailed: '加载 Box 状态失败',
|
||||||
failedToGetDebugInfo: '获取调试信息失败',
|
failedToGetDebugInfo: '获取调试信息失败',
|
||||||
copiedToClipboard: '已复制到剪贴板',
|
copiedToClipboard: '已复制到剪贴板',
|
||||||
deleting: '删除中...',
|
deleting: '删除中...',
|
||||||
|
|||||||
@@ -447,6 +447,15 @@ const zhHant = {
|
|||||||
debugKey: '偵錯金鑰',
|
debugKey: '偵錯金鑰',
|
||||||
noDebugKey: '(未設定)',
|
noDebugKey: '(未設定)',
|
||||||
debugKeyDisabled: '未設定偵錯金鑰,外掛偵錯無需認證',
|
debugKeyDisabled: '未設定偵錯金鑰,外掛偵錯無需認證',
|
||||||
|
boxStatusTitle: 'Box 執行時',
|
||||||
|
boxStatus: '狀態',
|
||||||
|
boxConnected: '已連線',
|
||||||
|
boxUnavailable: '不可用',
|
||||||
|
boxBackend: '後端',
|
||||||
|
boxProfile: '設定檔',
|
||||||
|
boxSessions: '工作階段',
|
||||||
|
boxErrors: '錯誤數',
|
||||||
|
boxStatusLoadFailed: '載入 Box 狀態失敗',
|
||||||
failedToGetDebugInfo: '取得偵錯資訊失敗',
|
failedToGetDebugInfo: '取得偵錯資訊失敗',
|
||||||
copiedToClipboard: '已複製到剪貼簿',
|
copiedToClipboard: '已複製到剪貼簿',
|
||||||
deleting: '刪除中...',
|
deleting: '刪除中...',
|
||||||
|
|||||||
Reference in New Issue
Block a user