mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-12 16:56:02 +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,
|
||||
Bug,
|
||||
Unlink,
|
||||
Box,
|
||||
CircleCheck,
|
||||
CircleX,
|
||||
Activity,
|
||||
} from 'lucide-react';
|
||||
import {
|
||||
DropdownMenu,
|
||||
@@ -43,7 +47,10 @@ import { httpClient } from '@/app/infra/http/HttpClient';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
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 {
|
||||
PluginInstallTaskQueue,
|
||||
@@ -133,6 +140,7 @@ function PluginListView() {
|
||||
const [debugPopoverOpen, setDebugPopoverOpen] = useState(false);
|
||||
const [copiedDebugUrl, setCopiedDebugUrl] = useState(false);
|
||||
const [copiedDebugKey, setCopiedDebugKey] = useState(false);
|
||||
const [boxStatus, setBoxStatus] = useState<ApiRespBoxStatus | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchPluginSystemStatus = async () => {
|
||||
@@ -454,8 +462,12 @@ function PluginListView() {
|
||||
|
||||
const handleShowDebugInfo = async () => {
|
||||
try {
|
||||
const info = await httpClient.getPluginDebugInfo();
|
||||
const [info, box] = await Promise.all([
|
||||
httpClient.getPluginDebugInfo(),
|
||||
httpClient.getBoxStatus().catch(() => null),
|
||||
]);
|
||||
setDebugInfo(info);
|
||||
setBoxStatus(box);
|
||||
setDebugPopoverOpen(true);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch debug info:', error);
|
||||
@@ -645,6 +657,82 @@ function PluginListView() {
|
||||
</p>
|
||||
)}
|
||||
</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>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
@@ -351,6 +351,19 @@ export interface ApiRespPluginSystemStatus {
|
||||
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 {
|
||||
tasks: AsyncTask[];
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ import {
|
||||
ApiRespProviderRerankModel,
|
||||
RerankModel,
|
||||
ApiRespPluginSystemStatus,
|
||||
ApiRespBoxStatus,
|
||||
ApiRespMCPServers,
|
||||
ApiRespMCPServer,
|
||||
MCPServer,
|
||||
@@ -889,6 +890,10 @@ export class BackendClient extends BaseHttpClient {
|
||||
return this.get('/api/v1/plugins/debug-info');
|
||||
}
|
||||
|
||||
public getBoxStatus(): Promise<ApiRespBoxStatus> {
|
||||
return this.get('/api/v1/box/status');
|
||||
}
|
||||
|
||||
// ============ User API ============
|
||||
public checkIfInited(): Promise<{ initialized: boolean }> {
|
||||
return this.get('/api/v1/user/init');
|
||||
|
||||
Reference in New Issue
Block a user