diff --git a/ui/src/components/error-logs-monitor.tsx b/ui/src/components/error-logs-monitor.tsx index 74418e3a..b399a67b 100644 --- a/ui/src/components/error-logs-monitor.tsx +++ b/ui/src/components/error-logs-monitor.tsx @@ -1,11 +1,11 @@ /** * Error Logs Monitor Component * - * Displays CLIProxyAPI error logs with expandable details. - * Designed to complement the AuthMonitor on the Home page. + * Displays CLIProxyAPI error logs with master-detail split view. + * Log list on left, content panel on right for better readability. */ -import { useState } from 'react'; +import { useState, useMemo } from 'react'; import { useCliproxyErrorLogs, useCliproxyErrorLogContent } from '@/hooks/use-cliproxy-stats'; import { useCliproxyStatus } from '@/hooks/use-cliproxy-stats'; import { cn, STATUS_COLORS } from '@/lib/utils'; @@ -13,14 +13,85 @@ import { Skeleton } from '@/components/ui/skeleton'; import { ScrollArea } from '@/components/ui/scroll-area'; import { AlertTriangle, - ChevronDown, - ChevronRight, FileWarning, Clock, FileText, XCircle, + FlaskConical, + Terminal, } from 'lucide-react'; +/** Demo mode mock data */ +const DEMO_ERROR_LOGS = [ + { + name: 'error-v1-chat-completions-2025-12-18T17-45-23.log', + size: 4523, + modified: Math.floor(Date.now() / 1000) - 120, + }, + { + name: 'error-v1-messages-2025-12-18T17-30-15.log', + size: 8912, + modified: Math.floor(Date.now() / 1000) - 900, + }, + { + name: 'error-v1-chat-completions-2025-12-18T16-22-08.log', + size: 2341, + modified: Math.floor(Date.now() / 1000) - 5400, + }, + { + name: 'error-v1-models-2025-12-18T14-10-55.log', + size: 1024, + modified: Math.floor(Date.now() / 1000) - 12600, + }, +]; + +const DEMO_LOG_CONTENT = `================================================================================ +REQUEST ERROR LOG +================================================================================ +Timestamp: 2025-12-18T17:45:23Z +Endpoint: /v1/chat/completions +Method: POST +Status: 429 Too Many Requests + +-------------------------------------------------------------------------------- +REQUEST HEADERS +-------------------------------------------------------------------------------- +Content-Type: application/json +Authorization: Bearer ccs-internal-managed +User-Agent: claude-code/1.0 + +-------------------------------------------------------------------------------- +REQUEST BODY +-------------------------------------------------------------------------------- +{ + "model": "gemini-claude-opus-4-5-thinking", + "messages": [ + {"role": "user", "content": "Explain quantum computing"} + ], + "max_tokens": 4096, + "stream": true +} + +-------------------------------------------------------------------------------- +RESPONSE +-------------------------------------------------------------------------------- +{ + "error": { + "message": "Rate limit exceeded. Please retry after 60 seconds.", + "type": "rate_limit_error", + "code": "rate_limit_exceeded" + } +} + +-------------------------------------------------------------------------------- +UPSTREAM API ERROR +-------------------------------------------------------------------------------- +Provider: gemini +Account: user@gmail.com +Quota: Daily limit reached (1500/1500 requests) +Retry-After: 60 +================================================================================`; + /** Format file size in human-readable format */ function formatSize(bytes: number): string { if (bytes < 1024) return `${bytes} B`; @@ -39,7 +110,6 @@ function formatRelativeTime(unixSeconds: number): string { /** Parse error log filename to extract endpoint and timestamp */ function parseErrorLogName(name: string): { endpoint: string; timestamp: string } { - // Format: error-v1-chat-completions-2025-01-15T10-30-00.log const match = name.match(/^error-(.+)-(\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2})\.log$/); if (match) { const endpoint = match[1].replace(/-/g, '/'); @@ -49,84 +119,181 @@ function parseErrorLogName(name: string): { endpoint: string; timestamp: string return { endpoint: name, timestamp: '' }; } -/** Error log content viewer with syntax highlighting */ -function ErrorLogContent({ name }: { name: string }) { - const { data: content, isLoading, error } = useCliproxyErrorLogContent(name); +/** Log content panel component */ +function LogContentPanel({ name, demo = false }: { name: string | null; demo?: boolean }) { + const { data: content, isLoading, error } = useCliproxyErrorLogContent(demo ? null : name); + // No log selected + if (!name) { + return ( +
Select a log to view details
+
+ {DEMO_LOG_CONTENT}
+
+ Failed to load log content
+
- {content}
-
-
+ {content}
+
+