Add new RAG dashboard feature including: - Main application layout with header and sidebar - Multiple pages (Home, KnowledgeBase, PipelineConfig, Dashboard, MCP) - Theme configuration and styling - Routing setup with protected routes - Login page and authentication flow - Various UI components and mock data for dashboard views
516 lines
18 KiB
TypeScript
516 lines
18 KiB
TypeScript
import React, { useState } from 'react';
|
|
import {
|
|
Box,
|
|
Typography,
|
|
Card,
|
|
CardContent,
|
|
Grid,
|
|
Button,
|
|
Switch,
|
|
FormControlLabel,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableContainer,
|
|
TableHead,
|
|
TableRow,
|
|
Chip,
|
|
IconButton,
|
|
Menu,
|
|
MenuItem,
|
|
TextField,
|
|
Dialog,
|
|
DialogTitle,
|
|
DialogContent,
|
|
DialogActions,
|
|
Alert,
|
|
Tabs,
|
|
Tab,
|
|
} from '@mui/material';
|
|
import {
|
|
Add as AddIcon,
|
|
MoreVert as MoreVertIcon,
|
|
Settings as SettingsIcon,
|
|
Link as LinkIcon,
|
|
Security as SecurityIcon,
|
|
Speed as SpeedIcon,
|
|
CheckCircle as CheckCircleIcon,
|
|
Error as ErrorIcon,
|
|
} from '@mui/icons-material';
|
|
import { styled } from '@mui/material/styles';
|
|
|
|
const PageContainer = styled(Box)(({ theme }) => ({
|
|
padding: '1.5rem',
|
|
backgroundColor: '#F8F9FA',
|
|
minHeight: 'calc(100vh - 60px)',
|
|
}));
|
|
|
|
const PageHeader = styled(Box)(({ theme }) => ({
|
|
display: 'flex',
|
|
justifyContent: 'space-between',
|
|
alignItems: 'center',
|
|
marginBottom: '1.5rem',
|
|
}));
|
|
|
|
const StatusCard = styled(Card)(({ theme }) => ({
|
|
height: '100%',
|
|
border: '1px solid #E5E5E5',
|
|
transition: 'all 0.2s ease-in-out',
|
|
'&:hover': {
|
|
boxShadow: '0 4px 12px rgba(0,0,0,0.1)',
|
|
},
|
|
}));
|
|
|
|
const StatusChip = styled(Chip)<{ status: string }>(({ status, theme }) => ({
|
|
fontSize: '0.75rem',
|
|
height: '24px',
|
|
backgroundColor:
|
|
status === 'connected' ? '#E8F5E8' :
|
|
status === 'connecting' ? '#FFF3CD' :
|
|
status === 'error' ? '#F8D7DA' : '#F8F9FA',
|
|
color:
|
|
status === 'connected' ? '#155724' :
|
|
status === 'connecting' ? '#856404' :
|
|
status === 'error' ? '#721C24' : '#666',
|
|
}));
|
|
|
|
const mockMCPServers = [
|
|
{
|
|
id: 1,
|
|
name: 'File System Server',
|
|
description: '文件系统操作服务器',
|
|
url: 'mcp://localhost:3001',
|
|
status: 'connected',
|
|
version: '1.0.0',
|
|
tools: ['read_file', 'write_file', 'list_directory'],
|
|
lastPing: '2024-01-15 14:30:25',
|
|
},
|
|
{
|
|
id: 2,
|
|
name: 'Database Server',
|
|
description: '数据库查询服务器',
|
|
url: 'mcp://db.example.com:3002',
|
|
status: 'connected',
|
|
version: '1.2.1',
|
|
tools: ['query_sql', 'execute_sql', 'get_schema'],
|
|
lastPing: '2024-01-15 14:30:20',
|
|
},
|
|
{
|
|
id: 3,
|
|
name: 'Web Scraper',
|
|
description: '网页抓取服务器',
|
|
url: 'mcp://scraper.example.com:3003',
|
|
status: 'connecting',
|
|
version: '0.9.5',
|
|
tools: ['scrape_url', 'extract_text', 'get_links'],
|
|
lastPing: '2024-01-15 14:28:15',
|
|
},
|
|
{
|
|
id: 4,
|
|
name: 'API Gateway',
|
|
description: 'API网关服务器',
|
|
url: 'mcp://api.example.com:3004',
|
|
status: 'error',
|
|
version: '2.1.0',
|
|
tools: ['call_api', 'auth_token', 'rate_limit'],
|
|
lastPing: '2024-01-15 14:25:10',
|
|
},
|
|
];
|
|
|
|
const MCP: React.FC = () => {
|
|
const [tabValue, setTabValue] = useState(0);
|
|
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
|
|
const [selectedServer, setSelectedServer] = useState<number | null>(null);
|
|
const [dialogOpen, setDialogOpen] = useState(false);
|
|
const [newServer, setNewServer] = useState({
|
|
name: '',
|
|
url: '',
|
|
description: '',
|
|
});
|
|
|
|
const handleTabChange = (event: React.SyntheticEvent, newValue: number) => {
|
|
setTabValue(newValue);
|
|
};
|
|
|
|
const handleMenuClick = (event: React.MouseEvent<HTMLElement>, serverId: number) => {
|
|
setAnchorEl(event.currentTarget);
|
|
setSelectedServer(serverId);
|
|
};
|
|
|
|
const handleMenuClose = () => {
|
|
setAnchorEl(null);
|
|
setSelectedServer(null);
|
|
};
|
|
|
|
const handleAddServer = () => {
|
|
setDialogOpen(true);
|
|
};
|
|
|
|
const handleDialogClose = () => {
|
|
setDialogOpen(false);
|
|
setNewServer({ name: '', url: '', description: '' });
|
|
};
|
|
|
|
const handleSaveServer = () => {
|
|
// 保存服务器逻辑
|
|
console.log('添加服务器:', newServer);
|
|
handleDialogClose();
|
|
};
|
|
|
|
const connectedServers = mockMCPServers.filter(s => s.status === 'connected').length;
|
|
const totalTools = mockMCPServers.reduce((acc, server) => acc + server.tools.length, 0);
|
|
|
|
return (
|
|
<PageContainer>
|
|
<PageHeader>
|
|
<Box>
|
|
<Typography variant="h4" fontWeight={600} color="#333">
|
|
MCP 协议管理
|
|
</Typography>
|
|
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
|
|
Model Context Protocol - 模型上下文协议服务器管理
|
|
</Typography>
|
|
</Box>
|
|
<Button
|
|
variant="contained"
|
|
startIcon={<AddIcon />}
|
|
onClick={handleAddServer}
|
|
sx={{ borderRadius: '6px' }}
|
|
>
|
|
添加服务器
|
|
</Button>
|
|
</PageHeader>
|
|
|
|
<Box sx={{ borderBottom: 1, borderColor: 'divider', mb: 3 }}>
|
|
<Tabs value={tabValue} onChange={handleTabChange}>
|
|
<Tab label="服务器管理" />
|
|
<Tab label="工具配置" />
|
|
<Tab label="协议监控" />
|
|
</Tabs>
|
|
</Box>
|
|
|
|
{tabValue === 0 && (
|
|
<>
|
|
{/* 状态概览 */}
|
|
<Grid container spacing={3} sx={{ mb: 3 }}>
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<StatusCard>
|
|
<CardContent>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Box>
|
|
<Typography color="text.secondary" variant="body2">
|
|
连接服务器
|
|
</Typography>
|
|
<Typography variant="h4" fontWeight={600} color="success.main">
|
|
{connectedServers}
|
|
</Typography>
|
|
</Box>
|
|
<CheckCircleIcon color="success" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
|
</Box>
|
|
</CardContent>
|
|
</StatusCard>
|
|
</Grid>
|
|
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<StatusCard>
|
|
<CardContent>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Box>
|
|
<Typography color="text.secondary" variant="body2">
|
|
总服务器数
|
|
</Typography>
|
|
<Typography variant="h4" fontWeight={600} color="primary">
|
|
{mockMCPServers.length}
|
|
</Typography>
|
|
</Box>
|
|
<LinkIcon color="primary" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
|
</Box>
|
|
</CardContent>
|
|
</StatusCard>
|
|
</Grid>
|
|
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<StatusCard>
|
|
<CardContent>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Box>
|
|
<Typography color="text.secondary" variant="body2">
|
|
可用工具
|
|
</Typography>
|
|
<Typography variant="h4" fontWeight={600} color="info.main">
|
|
{totalTools}
|
|
</Typography>
|
|
</Box>
|
|
<SettingsIcon color="info" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
|
</Box>
|
|
</CardContent>
|
|
</StatusCard>
|
|
</Grid>
|
|
|
|
<Grid item xs={12} sm={6} md={3}>
|
|
<StatusCard>
|
|
<CardContent>
|
|
<Box display="flex" alignItems="center" justifyContent="space-between">
|
|
<Box>
|
|
<Typography color="text.secondary" variant="body2">
|
|
平均延迟
|
|
</Typography>
|
|
<Typography variant="h4" fontWeight={600} color="warning.main">
|
|
45ms
|
|
</Typography>
|
|
</Box>
|
|
<SpeedIcon color="warning" sx={{ fontSize: '3rem', opacity: 0.3 }} />
|
|
</Box>
|
|
</CardContent>
|
|
</StatusCard>
|
|
</Grid>
|
|
</Grid>
|
|
|
|
{/* 服务器列表 */}
|
|
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
|
<CardContent>
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|
MCP 服务器列表
|
|
</Typography>
|
|
<TableContainer>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>服务器名称</TableCell>
|
|
<TableCell>URL</TableCell>
|
|
<TableCell>状态</TableCell>
|
|
<TableCell>版本</TableCell>
|
|
<TableCell>工具数量</TableCell>
|
|
<TableCell>最后心跳</TableCell>
|
|
<TableCell>操作</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{mockMCPServers.map((server) => (
|
|
<TableRow key={server.id} hover>
|
|
<TableCell>
|
|
<Box>
|
|
<Typography variant="body2" fontWeight={600}>
|
|
{server.name}
|
|
</Typography>
|
|
<Typography variant="caption" color="text.secondary">
|
|
{server.description}
|
|
</Typography>
|
|
</Box>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Typography variant="body2" fontFamily="monospace">
|
|
{server.url}
|
|
</Typography>
|
|
</TableCell>
|
|
<TableCell>
|
|
<StatusChip
|
|
status={server.status}
|
|
label={
|
|
server.status === 'connected' ? '已连接' :
|
|
server.status === 'connecting' ? '连接中' : '错误'
|
|
}
|
|
size="small"
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Chip
|
|
label={server.version}
|
|
size="small"
|
|
variant="outlined"
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Typography variant="body2">
|
|
{server.tools.length} 个工具
|
|
</Typography>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Typography variant="body2" color="text.secondary">
|
|
{server.lastPing}
|
|
</Typography>
|
|
</TableCell>
|
|
<TableCell>
|
|
<IconButton
|
|
size="small"
|
|
onClick={(e) => handleMenuClick(e, server.id)}
|
|
>
|
|
<MoreVertIcon />
|
|
</IconButton>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</TableContainer>
|
|
</CardContent>
|
|
</Card>
|
|
</>
|
|
)}
|
|
|
|
{tabValue === 1 && (
|
|
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
|
<CardContent>
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|
工具配置
|
|
</Typography>
|
|
<Alert severity="info" sx={{ mb: 2 }}>
|
|
配置各个 MCP 服务器提供的工具和权限设置
|
|
</Alert>
|
|
<Grid container spacing={2}>
|
|
{mockMCPServers.map((server) => (
|
|
<Grid item xs={12} md={6} key={server.id}>
|
|
<Card variant="outlined">
|
|
<CardContent>
|
|
<Typography variant="subtitle1" fontWeight={600} mb={1}>
|
|
{server.name}
|
|
</Typography>
|
|
<Box display="flex" flexDirection="column" gap={1}>
|
|
{server.tools.map((tool, index) => (
|
|
<FormControlLabel
|
|
key={index}
|
|
control={<Switch defaultChecked size="small" />}
|
|
label={
|
|
<Typography variant="body2" fontFamily="monospace">
|
|
{tool}
|
|
</Typography>
|
|
}
|
|
/>
|
|
))}
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
))}
|
|
</Grid>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{tabValue === 2 && (
|
|
<Grid container spacing={3}>
|
|
<Grid item xs={12} md={6}>
|
|
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
|
<CardContent>
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|
<SecurityIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
安全监控
|
|
</Typography>
|
|
<Box display="flex" flexDirection="column" gap={2}>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
<Typography variant="body2">SSL/TLS 加密</Typography>
|
|
<CheckCircleIcon color="success" />
|
|
</Box>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
<Typography variant="body2">身份验证</Typography>
|
|
<CheckCircleIcon color="success" />
|
|
</Box>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
<Typography variant="body2">访问控制</Typography>
|
|
<CheckCircleIcon color="success" />
|
|
</Box>
|
|
<Box display="flex" justifyContent="space-between" alignItems="center">
|
|
<Typography variant="body2">审计日志</Typography>
|
|
<ErrorIcon color="error" />
|
|
</Box>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
|
|
<Grid item xs={12} md={6}>
|
|
<Card sx={{ border: '1px solid #E5E5E5' }}>
|
|
<CardContent>
|
|
<Typography variant="h6" fontWeight={600} mb={2}>
|
|
<SpeedIcon sx={{ mr: 1, verticalAlign: 'middle' }} />
|
|
性能监控
|
|
</Typography>
|
|
<Box display="flex" flexDirection="column" gap={2}>
|
|
<Box>
|
|
<Box display="flex" justifyContent="space-between" mb={1}>
|
|
<Typography variant="body2">平均响应时间</Typography>
|
|
<Typography variant="body2" fontWeight={600}>45ms</Typography>
|
|
</Box>
|
|
<Box bgcolor="#F8F9FA" p={1} borderRadius="4px">
|
|
<Typography variant="caption" color="text.secondary">
|
|
过去24小时内的平均值
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
<Box>
|
|
<Box display="flex" justifyContent="space-between" mb={1}>
|
|
<Typography variant="body2">成功率</Typography>
|
|
<Typography variant="body2" fontWeight={600} color="success.main">
|
|
99.2%
|
|
</Typography>
|
|
</Box>
|
|
<Box bgcolor="#F8F9FA" p={1} borderRadius="4px">
|
|
<Typography variant="caption" color="text.secondary">
|
|
过去24小时内的成功率
|
|
</Typography>
|
|
</Box>
|
|
</Box>
|
|
</Box>
|
|
</CardContent>
|
|
</Card>
|
|
</Grid>
|
|
</Grid>
|
|
)}
|
|
|
|
{/* 菜单 */}
|
|
<Menu
|
|
anchorEl={anchorEl}
|
|
open={Boolean(anchorEl)}
|
|
onClose={handleMenuClose}
|
|
>
|
|
<MenuItem onClick={handleMenuClose}>
|
|
<SettingsIcon sx={{ mr: 1 }} fontSize="small" />
|
|
配置
|
|
</MenuItem>
|
|
<MenuItem onClick={handleMenuClose}>测试连接</MenuItem>
|
|
<MenuItem onClick={handleMenuClose}>查看日志</MenuItem>
|
|
<MenuItem onClick={handleMenuClose} sx={{ color: 'error.main' }}>
|
|
断开连接
|
|
</MenuItem>
|
|
</Menu>
|
|
|
|
{/* 添加服务器对话框 */}
|
|
<Dialog open={dialogOpen} onClose={handleDialogClose} maxWidth="sm" fullWidth>
|
|
<DialogTitle>添加 MCP 服务器</DialogTitle>
|
|
<DialogContent>
|
|
<Box display="flex" flexDirection="column" gap={2} pt={1}>
|
|
<TextField
|
|
fullWidth
|
|
label="服务器名称"
|
|
value={newServer.name}
|
|
onChange={(e) => setNewServer(prev => ({ ...prev, name: e.target.value }))}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
label="服务器 URL"
|
|
placeholder="mcp://localhost:3000"
|
|
value={newServer.url}
|
|
onChange={(e) => setNewServer(prev => ({ ...prev, url: e.target.value }))}
|
|
/>
|
|
<TextField
|
|
fullWidth
|
|
label="描述"
|
|
multiline
|
|
rows={3}
|
|
value={newServer.description}
|
|
onChange={(e) => setNewServer(prev => ({ ...prev, description: e.target.value }))}
|
|
/>
|
|
</Box>
|
|
</DialogContent>
|
|
<DialogActions>
|
|
<Button onClick={handleDialogClose}>取消</Button>
|
|
<Button onClick={handleSaveServer} variant="contained">
|
|
添加
|
|
</Button>
|
|
</DialogActions>
|
|
</Dialog>
|
|
</PageContainer>
|
|
);
|
|
};
|
|
|
|
export default MCP; |