# 第5章 项目架构深度解析

## 作者

**谭策** — 独立开发者 | AIOps 领域探索者

- 🌐 项目官网：[ITOpsAgentinfo](https://www.zjzwfw.cloud/ITOpsAgentinfo)
- 📝 博客：[zjzwfw.cloud](https://www.zjzwfw.cloud/)
- 📧 邮箱：<huawei_network@foxmail.com>
- 💬 微信公众号：**IT Online**

<p align="left">
  <img src="../../frontend/public/wechaterweima.png" width="200" alt="IT Online 微信公众号">
</p>

## 许可证

[MPL-2.0](../../LICENSE) © 谭策

## 本章导读

在前面的章节中，我们了解了项目的全貌和各项技术栈。本章将深入项目的**内部架构**，理解各个模块是如何组织、通信和协作的。这是从"使用者"到"开发者"的关键一步。

**类比理解**：如果前三章让你了解了这栋楼的外观、周边环境和入住流程，本章就是带你参观建筑的施工图纸、管道线路、电路布局和承重结构。只有理解了这些，你才能放心地装修（二次开发）而不影响整体安全。

## 学习目标

阅读完本章后，你将能够：
- 画出项目的完整架构图，说明各层职责
- 理解后端的路由 → 中间件 → 服务 → 数据库的请求链路
- 理解前端的路由 → 页面 → 组件 → API 调用的数据流
- 说明前后端之间的通信方式（HTTP + WebSocket）
- 理解项目的服务初始化和优雅关闭机制
- 独立定位项目中的任何一个功能模块

---

## 5.1 整体架构总览

### 5.1.1 三层架构模型

项目采用经典的**三层架构**（Three-Tier Architecture），将系统划分为三个独立的层级：

```
┌─────────────────────────────────────────────────────────────────────┐
│                         表现层 (Presentation Tier)                    │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │                    React 前端应用                              │  │
│  │  ┌─────────┐ ┌──────────┐ ┌──────────┐ ┌────────────┐       │  │
│  │  │ 页面组件 │ │ 通用组件 │ │ 状态管理 │ │ HTTP客户端 │       │  │
│  │  └─────────┘ └──────────┘ └──────────┘ └────────────┘       │  │
│  │                                                               │  │
│  │  技术：React + TypeScript + Vite + Tailwind CSS             │  │
│  └───────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
                                │
                    HTTP REST API + WebSocket
                                │
┌─────────────────────────────────────────────────────────────────────┐
│                         逻辑层 (Logic Tier)                          │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │                   Express 后端应用                             │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐        │  │
│  │  │ 路由层   │→│ 中间件层 │→│ 服务层   │→│ 数据模型 │        │  │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘        │  │
│  │                                                               │  │
│  │  技术：Node.js + Express + TypeScript + SQLite              │  │
│  └───────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
                                │
                         文件/进程
                                │
┌─────────────────────────────────────────────────────────────────────┐
│                         数据层 (Data Tier)                           │
│  ┌───────────────────────────────────────────────────────────────┐  │
│  │                    SQLite 数据库                               │  │
│  │  ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐        │  │
│  │  │ 用户表   │ │ 服务器表 │ │ 任务表   │ │ 告警表   │        │  │
│  │  └──────────┘ └──────────┘ └──────────┘ └──────────┘        │  │
│  │                                                               │  │
│  │  技术：better-sqlite3 (同步 SQLite 驱动)                      │  │
│  └───────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────┘
```

### 5.1.2 架构设计原则

| 原则 | 说明 | 项目中的体现 |
|------|------|-------------|
| 关注点分离 | 每层只关心自己的职责 | 前端管 UI，后端管业务逻辑，数据库管存储 |
| 高内聚低耦合 | 模块内部紧密相关，模块之间松耦合 | 服务层之间通过接口调用，不直接依赖实现 |
| 依赖倒置 | 高层模块不依赖低层模块 | 路由层依赖服务接口，不依赖具体数据库实现 |
| 单一职责 | 每个模块只做一件事 | 每个 service 文件只负责一个业务域 |

### 5.1.3 模块通信图

```
浏览器
  │
  ├─ HTTP 请求 ──────────────────────────────→ Express 路由
  │                                              │
  │                                         中间件链
  │                                              │
  │                                         业务服务
  │                                              │
  │                                         SQLite 数据库
  │                                              │
  │                                         响应数据
  │                                              │
  │ ←────────────────────────────────────────── JSON 响应
  │
  ├─ WebSocket 连接 ─────────────────────────→ Socket.io 服务
  │                                              │
  │                                         SSH 连接 → 目标服务器
  │                                              │
  │ ←─────────────────────────────────────── 实时数据推送
```

---

## 5.2 后端架构详解

### 5.2.1 后端目录结构

```
backend/src/
├── app.ts                    # 应用入口：Express 初始化 + 中间件 + 路由注册
├── routes/                   # 路由层（32 个路由文件）
│   ├── authRoutes.ts         # 认证路由
│   ├── agentRoutes.ts        # Agent 管理
│   ├── workflowRoutes.ts     # 工作流管理
│   ├── serverRoutes.ts       # 服务器管理
│   ├── alertRoutes.ts        # 告警管理
│   ├── ...
│   └── webhookRoutes.ts      # 外部系统 Webhook
├── middleware/               # 中间件层
│   ├── auth.ts              # JWT 认证中间件
│   ├── rateLimiter.ts       # 请求频率限制
│   ├── trace.ts             # 请求追踪
│   ├── commandFilter.ts     # SSH 命令过滤
│   ├── validation.ts        # 请求验证
│   └── errorHandler.ts      # 全局错误处理
├── services/                # 业务逻辑层（24+ 个服务）
│   ├── llmService.ts        # LLM 调用服务
│   ├── sshService.ts        # SSH 连接服务
│   ├── workflowExecutor.ts  # 工作流执行引擎
│   ├── alertService.ts      # 告警处理服务
│   ├── agentExecutor.ts     # Agent 执行服务
│   ├── copilotService.ts    # AI Copilot 服务
│   ├── schedulerService.ts  # 定时任务调度
│   └── ...
├── models/                  # 数据模型层
│   ├── database.ts          # 数据库初始化 + 表定义
│   ├── migrations.ts        # 数据库迁移
│   └── presets/             # 预设数据初始化
│       ├── initAgents.ts
│       ├── initWorkflows.ts
│       └── ...
├── websocket/               # WebSocket 处理
│   └── handler.ts           # WebSocket 事件处理
├── schemas/                 # 数据验证 Schema
│   └── apiValidation.ts     # API 参数验证
├── types/                   # TypeScript 类型定义
│   ├── index.ts
│   └── errors.ts
└── utils/                   # 工具函数
    ├── logger.ts            # 日志工具
    ├── env.ts               # 环境变量读取
    ├── apiConfig.ts         # API 配置
    ├── retry.ts             # 重试工具
    └── sensitiveMask.ts     # 敏感信息脱敏
```

### 5.2.2 应用启动流程

后端启动是一个**有序的初始化过程**，在 `app.ts` 中按以下步骤执行：

```
1. 创建 Express 应用
   ↓
2. 创建 HTTP 服务器 + Socket.io 实例
   ↓
3. 注册全局中间件（Helmet、CORS、BodyParser、Morgan）
   ↓
4. 初始化数据库（创建表 + 预设数据）
   ↓
5. 初始化各业务服务
   ├── initAlertService()    - 告警服务初始化
   ├── reportService.init()  - 报告服务
   ├── copilotService.init() - Copilot 服务
   ├── schedulerService.init() - 定时任务调度
   ├── notificationService.init() - 通知服务
   ├── remediationService.init() - 修复服务
   ├── rootCauseAnalysisService.init() - 根因分析服务
   ├── backupService.init()  - 备份服务
   └── initTokenBlacklist()  - Token 黑名单
   ↓
6. 设置 WebSocket 连接处理
   ↓
7. 注册路由（公开路由 → 受保护路由）
   ↓
8. 注册错误处理中间件
   ↓
9. 启动 HTTP 服务器（监听端口）
   ↓
10. 注册进程信号处理（SIGTERM、SIGINT）
```

**关键代码分析**：

```typescript
// 1. 创建 Express 应用和 HTTP 服务器
const app = express();
const httpServer = createServer(app);

// 2. 创建 Socket.io 实例，绑定到 HTTP 服务器
const io = new SocketIOServer(httpServer, {
  cors: {
    origin: env.ALLOWED_ORIGINS,  // 允许的跨域来源
    methods: ['GET', 'POST']
  },
  maxHttpBufferSize: 1e6,         // 最大消息 1MB
  pingTimeout: 60000,             // 心跳超时 60 秒
  pingInterval: 25000             // 心跳间隔 25 秒
});

// 3. 中间件注册顺序很重要！
app.use(helmet());                // 安全头（最先注册）
app.use(traceMiddleware);         // 请求追踪
app.use(morgan('combined'));      // 访问日志
app.use(cors({ ... }));           // 跨域配置
app.use(bodyParser.json({ ... })); // 请求体解析
```

**为什么中间件的注册顺序很重要？**

中间件按注册顺序依次执行，就像流水线上的工人：

```
请求进入
  ↓
[第1个] Helmet → 添加安全头
  ↓
[第2个] Trace → 记录请求 ID
  ↓
[第3个] Morgan → 记录访问日志
  ↓
[第4个] CORS → 处理跨域
  ↓
[第5个] BodyParser → 解析 JSON/表单数据
  ↓
[第6个] 路由 → 执行具体业务逻辑
```

如果 CORS 在 BodyParser 之后注册，跨域预检请求（OPTIONS）可能无法正确返回。

### 5.2.3 路由设计模式

项目中的路由采用**模块化设计**：每个功能域一个独立的路由文件。

**路由文件的标准结构**：

```typescript
// backend/src/routes/agentRoutes.ts 示例
import { Router } from 'express';

const router = Router();

// GET /api/agents - 获取所有 Agent
router.get('/', async (req, res, next) => {
  try {
    const agents = db.prepare('SELECT * FROM agents').all();
    res.json({ success: true, data: agents });
  } catch (error) {
    next(error);  // 交给错误处理中间件
  }
});

// POST /api/agents - 创建 Agent
router.post('/', async (req, res, next) => {
  try {
    const { name, description, systemPrompt } = req.body;
    const result = db.prepare(
      'INSERT INTO agents (name, description, system_prompt) VALUES (?, ?, ?)'
    ).run(name, description, systemPrompt);
    
    res.status(201).json({ success: true, data: { id: result.lastInsertRowid } });
  } catch (error) {
    next(error);
  }
});

// PUT /api/agents/:id - 更新 Agent
router.put('/:id', async (req, res, next) => {
  // ...
});

// DELETE /api/agents/:id - 删除 Agent
router.delete('/:id', async (req, res, next) => {
  // ...
});

export default router;
```

**路由注册到应用**：

```typescript
// app.ts 中
app.use('/api/agents', rateLimiter, agentRoutes);
```

这里发生了三件事：
1. 所有以 `/api/agents` 开头的请求都会经过这个中间件
2. 先经过 `rateLimiter` 频率限制
3. 再交给 `agentRoutes` 中的具体路由处理

### 5.2.4 服务层设计

服务层是后端的**核心业务逻辑**所在。每个服务负责一个独立的业务域。

**服务的设计模式**：

```typescript
// 服务通常是一个对象，包含多个方法
export const agentService = {
  // 初始化方法
  init(): void {
    // 加载配置、建立连接等
  },

  // 查询方法
  getAll(): Agent[] {
    return db.prepare('SELECT * FROM agents').all();
  },

  getById(id: number): Agent | undefined {
    return db.prepare('SELECT * FROM agents WHERE id = ?').get(id);
  },

  // 创建方法
  create(data: CreateAgentInput): Agent {
    const result = db.prepare(
      'INSERT INTO agents (name, system_prompt) VALUES (?, ?)'
    ).run(data.name, data.systemPrompt);
    return this.getById(result.lastInsertRowid as number);
  },

  // 执行方法（核心业务逻辑）
  async execute(agentId: number, input: string): Promise<string> {
    const agent = this.getById(agentId);
    if (!agent) throw new Error('Agent not found');

    // 调用 LLM API
    const response = await llmService.callLLM(agent.systemPrompt, input);
    return response;
  },
};
```

**服务间的依赖关系**：

```
llmService.ts          ← 被多个服务依赖（调用 AI 模型）
    ↑
    ├── agentExecutor.ts     ← Agent 执行器
    ├── copilotService.ts    ← AI Copilot
    ├── workflowExecutor.ts  ← 工作流执行器
    └── alertService.ts      ← 告警处理

sshService.ts          ← SSH 连接管理
    ↑
    ├── terminalService.ts   ← 终端服务
    └── serverInfoCollector.ts ← 服务器信息收集

schedulerService.ts    ← 定时任务调度
    ↑
    └── 定时触发各种服务
```

### 5.2.5 错误处理机制

项目采用**集中式错误处理**模式：

```typescript
// 404 处理
export const notFoundHandler = (req, res, next) => {
  res.status(404).json({
    success: false,
    error: `Route ${req.method} ${req.originalUrl} not found`
  });
};

// 全局错误处理
export const errorHandler = (err, req, res, next) => {
  const statusCode = err.statusCode || 500;
  const message = err.message || 'Internal Server Error';

  // 记录错误日志
  logger.error(`${req.method} ${req.originalUrl}`, {
    error: message,
    stack: err.stack,
    traceId: req.traceId
  });

  // 生产环境不暴露堆栈信息
  res.status(statusCode).json({
    success: false,
    error: env.NODE_ENV === 'production' ? message : err.stack
  });
};
```

**错误处理流程**：

```
路由处理函数抛出错误
  ↓
throw new Error('Agent not found')
  ↓
Express 捕获错误
  ↓
errorHandler 中间件
  ↓
记录日志 + 返回统一格式的错误响应
  ↓
{ "success": false, "error": "Agent not found" }
```

### 5.2.6 优雅关闭机制

项目在收到退出信号时，会执行**有序的关闭流程**：

```typescript
const gracefulShutdown = async (signal: string) => {
  logger.info(`${signal} received, starting graceful shutdown...`);
  
  // 设置超时保护（30 秒）
  const shutdownTimeout = setTimeout(() => {
    logger.error('Graceful shutdown timed out, forcing exit');
    process.exit(1);
  }, 30000);

  try {
    // 1. 关闭 HTTP 服务器（不再接受新请求）
    await new Promise<void>((resolve) => httpServer.close(() => resolve()));
    
    // 2. 关闭 WebSocket 服务器
    await new Promise<void>((resolve) => io.close(() => resolve()));
    
    // 3. 停止定时任务调度
    schedulerService.shutdown();
    
    // 4. 停止自动备份
    backupService.stopAutoBackup();
    
    // 5. 关闭数据库连接
    db.close();
    
    // 6. 关闭日志
    logger.shutdown();
    
    clearTimeout(shutdownTimeout);
    process.exit(0);
  } catch (error) {
    logger.error('Error during shutdown', error as Error);
    clearTimeout(shutdownTimeout);
    process.exit(1);
  }
};

// 注册信号处理
process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
process.on('SIGINT', () => gracefulShutdown('SIGINT'));
```

**为什么需要优雅关闭？**

| 场景 | 不优雅关闭 | 优雅关闭 |
|------|-----------|---------|
| 正在处理请求 | 请求中断，数据丢失 | 等待请求完成后再关闭 |
| 数据库写入 | 事务中断，数据损坏 | 等待事务提交 |
| SSH 连接 | 连接异常断开 | 正常关闭 SSH 连接 |
| 定时任务 | 任务执行到一半被杀掉 | 等待当前任务完成 |

---

## 5.3 前端架构详解

### 5.3.1 前端目录结构

```
frontend/src/
├── main.tsx                 # 应用入口：React DOM 渲染
├── App.tsx                  # 路由配置 + 应用结构
├── index.css                # 全局样式 + Tailwind 指令
├── vite-env.d.ts            # Vite 类型声明
├── contexts/                # React Context
│   └── AuthContext.tsx      # 认证上下文（登录状态、用户信息）
├── hooks/                   # 自定义 Hooks
│   └── useTheme.ts          # 主题切换 Hook
├── lib/                     # 工具库
│   ├── api.ts               # API 客户端（Axios 封装）
│   └── xss.ts               # XSS 防护工具
├── components/              # 通用组件
│   ├── layout/
│   │   └── Layout.tsx       # 主布局（侧边栏 + 顶栏 + 内容区）
│   ├── ProtectedRoute.tsx   # 路由守卫组件
│   ├── WebTerminal.tsx      # Web SSH 终端组件
│   ├── ChatWidget.tsx       # AI Copilot 聊天组件
│   ├── MarkdownOutput.tsx   # Markdown 渲染组件
│   ├── ErrorBoundary.tsx    # 错误边界组件
│   ├── AnimatedBarChart.tsx # 动画柱状图
│   ├── AnimatedLineChart.tsx# 动画折线图
│   ├── CircularProgress.tsx # 环形进度条
│   ├── ParticleBackground.tsx # 粒子背景
│   └── ImportExport.tsx     # 导入导出组件
└── pages/                   # 页面组件（26 个页面）
    ├── Dashboard.tsx         # 仪表盘
    ├── Login.tsx             # 登录页
    ├── Servers.tsx           # 服务器管理
    ├── Agents.tsx            # Agent 管理
    ├── WorkflowEditor.tsx    # 工作流编辑器
    ├── Workflows.tsx         # 工作流列表
    ├── Alerts.tsx            # 告警中心
    ├── Tasks.tsx             # 任务列表
    ├── Reports.tsx           # 报告中心
    ├── Knowledge.tsx         # 知识库
    ├── AuditLogs.tsx         # 审计日志
    ├── Settings.tsx          # 系统设置
    ├── Users.tsx             # 用户管理
    ├── Scripts.tsx           # 脚本管理
    ├── Notifications.tsx     # 通知管理
    ├── ScheduledTasks.tsx    # 定时任务
    ├── TerminalPage.tsx      # SSH 终端页
    ├── NotFound.tsx          # 404 页面
    └── ...                   # 其他页面
```

### 5.3.2 前端启动流程

```
main.tsx 启动
  ↓
创建 QueryClient（React Query 配置）
  ↓
创建 React 应用根节点
  ↓
<QueryClientProvider> 包裹
  ↓
<AuthProvider> 包裹
  ↓
<App /> 渲染
  ↓
<BrowserRouter> 初始化路由
  ↓
根据 URL 匹配对应页面组件
```

**main.tsx 入口代码**：

```typescript
import React from 'react';
import ReactDOM from 'react-dom/client';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { AuthProvider } from './contexts/AuthContext';
import App from './App';
import './index.css';

const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      staleTime: 1000 * 60 * 5,  // 5 分钟缓存
      retry: 2,                   // 失败重试 2 次
    },
  },
});

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <QueryClientProvider client={queryClient}>
      <AuthProvider>
        <App />
      </AuthProvider>
    </QueryClientProvider>
  </React.StrictMode>
);
```

### 5.3.3 路由设计

项目使用 **React Router v6** 管理前端路由：

```typescript
// App.tsx 路由配置
function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* 公开路由 */}
        <Route path="/login" element={<Login />} />
        
        {/* 受保护路由 */}
        <Route path="/" element={
          <ProtectedRoute>
            <Layout />
          </ProtectedRoute>
        }>
          <Route index element={<Dashboard />} />
          <Route path="servers" element={<Servers />} />
          <Route path="agents" element={<Agents />} />
          <Route path="workflows" element={<Workflows />} />
          <Route path="workflow-editor" element={<WorkflowEditor />} />
          <Route path="alerts" element={<Alerts />} />
          <Route path="tasks" element={<Tasks />} />
          <Route path="reports" element={<Reports />} />
          <Route path="knowledge" element={<Knowledge />} />
          <Route path="terminal" element={<TerminalPage />} />
          <Route path="audit" element={<AuditLogs />} />
          <Route path="settings" element={<Settings />} />
          {/* ... 更多路由 */}
        </Route>
        
        {/* 404 */}
        <Route path="*" element={<NotFound />} />
      </Routes>
    </BrowserRouter>
  );
}
```

**路由保护机制**：

```
访问 /servers
  ↓
<ProtectedRoute> 检查
  ↓
localStorage 中有 Token？
  ├─ 是 → 渲染 <Layout /> → 匹配 <Route path="servers"> → 显示 Servers 页面
  └─ 否 → 重定向到 /login
```

### 5.3.4 组件架构

项目中的组件分为三类：

| 类型 | 位置 | 职责 | 示例 |
|------|------|------|------|
| 页面组件 | `src/pages/` | 对应一个路由，包含完整业务逻辑 | `Dashboard.tsx` |
| 通用组件 | `src/components/` | 可复用的 UI 组件，无路由依赖 | `WebTerminal.tsx` |
| 布局组件 | `src/components/layout/` | 包裹页面的框架结构 | `Layout.tsx` |

**组件层次结构**：

```
App
└── BrowserRouter
    └── Routes
        └── Route (protected)
            └── ProtectedRoute
                └── Layout          ← 布局组件
                    ├── Sidebar     ← 侧边导航
                    ├── TopBar      ← 顶部栏
                    └── Outlet      ← 页面内容
                        └── Dashboard  ← 页面组件
                            ├── StatCard  ← 通用组件
                            ├── AnimatedBarChart ← 通用组件
                            └── ChatWidget     ← 通用组件
```

### 5.3.5 数据流模式

前端采用**多源数据管理**策略：

```
┌────────────────────────────────────────────────────┐
│                  前端数据流架构                       │
│                                                     │
│  服务器数据 (Servers, Agents, Tasks, ...)            │
│       ↑                                             │
│       │  useQuery / useMutation                     │
│       │                                             │
│       ├────→ React Query 缓存层                      │
│       │                                             │
│       │  api.get() / api.post()                     │
│       │                                             │
│       └────→ Axios (带拦截器)                        │
│                    │                                │
│                    ↓                                │
│               后端 API                               │
│                                                     │
│  全局 UI 状态 (侧边栏展开、主题)                       │
│       ↑                                             │
│       │  useStore()                                 │
│       │                                             │
│       └────→ Zustand Store                         │
│                                                     │
│  认证状态 (Token, 用户信息)                           │
│       ↑                                             │
│       │  useAuth()                                  │
│       │                                             │
│       └────→ AuthContext (React Context)            │
│                                                     │
│  表单状态                                            │
│       ↑                                             │
│       │  useState                                   │
│       │                                             │
│       └────→ 组件内部状态                             │
└────────────────────────────────────────────────────┘
```

### 5.3.6 API 客户端封装

项目使用 Axios 封装了统一的 API 客户端：

```typescript
// frontend/src/lib/api.ts
import axios from 'axios';

const api = axios.create({
  baseURL: '/api',
  timeout: 30000,
});

// 请求拦截器
api.interceptors.request.use((config) => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});

// 响应拦截器
api.interceptors.response.use(
  (response) => response.data,
  (error) => {
    if (error.response?.status === 401) {
      // Token 过期或无效，清除本地存储并跳转登录
      localStorage.removeItem('token');
      localStorage.removeItem('user');
      window.location.href = '/login';
    }
    return Promise.reject(error.response?.data || error);
  }
);

export default api;
```

**使用方式**：

```typescript
// 在页面组件中
import api from '../lib/api';

function Servers() {
  const { data: servers } = useQuery({
    queryKey: ['servers'],
    queryFn: () => api.get('/servers'),
  });

  const deleteServer = (id: string) => {
    api.delete(`/servers/${id}`);
    queryClient.invalidateQueries({ queryKey: ['servers'] });
  };
}
```

---

## 5.4 WebSocket 架构

### 5.4.1 WebSocket 连接流程

```
前端连接
  ↓
socket.io-client 发起 WebSocket 连接
  ↓
后端 Socket.io 接收连接
  ↓
setupWebSocket(io) 注册事件监听
  ↓
连接建立，开始双向通信
```

### 5.4.2 WebSocket 事件设计

项目中的 WebSocket 事件按功能域分组：

```
WebSocket 事件树:
├── connection         ← 客户端连接
├── disconnect         ← 客户端断开
│
├── SSH 终端
│   ├── terminal:input      ← 前端 → 后端：终端输入
│   ├── terminal:output     ← 后端 → 前端：终端输出
│   ├── terminal:resize     ← 前端 → 后端：终端大小变化
│   └── terminal:disconnect ← 前端 → 后端：断开 SSH 连接
│
├── Agent 执行
│   └── agent:progress      ← 后端 → 前端：执行进度
│
├── 工作流执行
│   └── workflow:progress   ← 后端 → 前端：节点执行进度
│
└── 告警通知
    └── alert:new           ← 后端 → 前端：新告警
```

### 5.4.3 WebSocket 处理代码结构

```typescript
// backend/src/websocket/handler.ts 模式
export function setupWebSocket(io: Server) {
  io.on('connection', (socket) => {
    logger.info(`WebSocket connected: ${socket.id}`);

    // SSH 终端事件
    socket.on('terminal:input', async (data) => {
      const { serverId, command } = data;
      // 获取 SSH 连接
      const connection = getSSHConnection(serverId);
      // 执行命令
      connection.write(command + '\n');
    });

    // 客户端断开
    socket.on('disconnect', () => {
      logger.info(`WebSocket disconnected: ${socket.id}`);
      // 清理 SSH 连接
      closeSSHConnections(socket.id);
    });
  });
}
```

---

## 5.5 数据库架构

### 5.5.1 数据库初始化流程

```
应用启动
  ↓
initializeDatabase()
  ↓
1. 打开/创建 SQLite 数据库文件
  ↓
2. 启用 WAL 模式（预写式日志）
  ↓
3. 创建所有数据表（如果不存在）
   ├── users 表
   ├── servers 表
   ├── agents 表
   ├── workflows 表
   └── ... (27 张直接创建)
   ↓
4. 执行数据库迁移（追加 alert_noise_reduction、remediation_policies 等 6 张迁移表）
   ↓
5. 插入预设数据（如果是首次初始化）
   ├── 9 个预设 Agent
   ├── 6 个预设工作流
   ├── 知识库条目
   └── 告警映射规则
   ↓
数据库就绪
```

### 5.5.2 数据库操作模式

项目使用 **better-sqlite3**，这是一个**同步**的 SQLite 驱动：

```typescript
import Database from 'better-sqlite3';

const db = new Database('./data/itops.db', {
  verbose: console.log  // 可选：打印 SQL 日志
});

// 启用 WAL 模式
db.pragma('journal_mode = WAL');

// 查询 - 单行
const user = db.prepare('SELECT * FROM users WHERE id = ?').get(1);

// 查询 - 多行
const servers = db.prepare('SELECT * FROM servers').all();

// 插入
const result = db.prepare(
  'INSERT INTO servers (name, host, port) VALUES (?, ?, ?)'
).run('prod-server', '192.168.1.1', 22);

// 更新
db.prepare('UPDATE servers SET status = ? WHERE id = ?').run('online', 1);

// 删除
db.prepare('DELETE FROM servers WHERE id = ?').run(1);

// 事务（多条操作要么全成功，要么全失败）
const transaction = db.transaction((data) => {
  db.prepare('INSERT INTO servers ...').run(data.server);
  db.prepare('INSERT INTO audit_logs ...').run(data.log);
});
transaction.run({ server: {...}, log: {...} });
```

**为什么选择同步驱动？**

| 特性 | better-sqlite3（同步） | sqlite3（异步） |
|------|----------------------|----------------|
| API 简洁度 | 同步调用，代码更简洁 | 回调/Promise，代码嵌套 |
| 性能 | 单线程场景下性能更好 | 异步开销 |
| 事务支持 | 原生同步事务 | 需要手动管理 |
| 适用场景 | 中小型应用（本项目） | 高并发场景 |

---

## 5.6 中间件体系

### 5.6.1 中间件清单

| 中间件 | 位置 | 用途 | 注册方式 |
|--------|------|------|---------|
| `helmet()` | 全局 | 设置安全相关的 HTTP 头 | `app.use()` |
| `traceMiddleware` | 全局 | 为每个请求生成唯一追踪 ID | `app.use()` |
| `morgan('combined')` | 全局 | 记录 HTTP 请求日志 | `app.use()` |
| `cors()` | 全局 | 处理跨域资源共享 | `app.use()` |
| `bodyParser.json()` | 全局 | 解析 JSON 请求体 | `app.use()` |
| `rateLimiter` | 路由组 | 限制请求频率 | `app.use('/api/*', rateLimiter)` |
| `authenticateToken` | 路由组 | JWT 身份验证 | `app.use(authenticateToken)` |

### 5.6.2 JWT 认证中间件详解

```typescript
// backend/src/middleware/auth.ts
export const authenticateToken = (req, res, next) => {
  // 1. 从请求头获取 Token
  const authHeader = req.headers.authorization;
  const token = authHeader?.split(' ')[1];  // "Bearer <token>"

  if (!token) {
    return res.status(401).json({
      success: false,
      error: '认证失败：未提供访问令牌'
    });
  }

  // 2. 验证 Token
  try {
    const decoded = jwt.verify(token, env.JWT_SECRET);
    req.user = decoded;  // 将用户信息挂载到请求对象
    next();              // 继续下一步
  } catch (error) {
    return res.status(403).json({
      success: false,
      error: '认证失败：令牌无效或已过期'
    });
  }
};
```

**认证流程可视化**：

```
请求: GET /api/servers
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
  ↓
authenticateToken 中间件
  ↓
提取 Token → 验证签名 → 检查有效期
  ↓
├─ 有效 → req.user = { id: 1, role: 'admin' } → next() → 路由处理
└─ 无效 → 返回 401/403 → 前端收到错误 → 跳转登录页
```

---

## 5.7 项目结构对比

### 5.7.1 前后端结构对照

| 维度 | 前端 | 后端 |
|------|------|------|
| 入口文件 | `main.tsx` | `app.ts` |
| 路由管理 | React Router v6 | Express Router |
| 页面/路由 | `src/pages/` (26 个) | `routes/` (31 个) |
| 通用组件 | `src/components/` | `middleware/` |
| 状态管理 | Zustand + React Query + Context | 内存 + SQLite |
| API 调用 | `lib/api.ts` (Axios) | 直接处理请求 |
| 工具函数 | `lib/`, `hooks/` | `utils/` |
| 类型定义 | 内联定义 | `types/` 目录 |

### 5.7.2 模块数量统计

| 层级 | 数量 | 说明 |
|------|------|------|
| 前端页面 | 26 个 | 对应 26 个路由 |
| 前端通用组件 | 11 个 | 跨页面复用 |
| 后端路由文件 | 32 个 | 对应 32 个路由组 |
| 后端服务文件 | 24+ 个 | 核心业务逻辑 |
| 后端中间件 | 6 个 | 认证、限流、错误处理等 |
| 数据库表 | 33 张 | 27张直接创建 + 6张迁移追加 |
| 预设数据 | 9 个 Agent + 6 个工作流 | 开箱即用 |

---

## 本章小结

本章深入解析了项目架构的各个方面：

1. **整体架构**：三层架构（表现层、逻辑层、数据层），层次清晰，职责分明
2. **后端架构**：Express 应用 → 中间件链 → 模块化路由 → 服务层 → SQLite，共 32 个路由组、24+ 个服务
3. **前端架构**：React Router → 路由保护 → 页面组件 → 通用组件，共 26 个页面、11 个通用组件
4. **WebSocket 架构**：Socket.io 双向通信，支持 SSH 终端、Agent 执行、工作流进度、告警通知
5. **数据库架构**：better-sqlite3 同步驱动，33 张表（27张直接创建 + 6张迁移追加），WAL 模式优化
6. **中间件体系**：Helmet、CORS、JWT 认证、频率限制、错误处理等
7. **启动和关闭**：有序的初始化流程 + 优雅的关闭机制

**核心理念**：关注点分离、高内聚低耦合、单一职责。

---

## 本章练习

### 基础练习

1. **架构绘制**：不查看代码，凭记忆画出项目的三层架构图，标注各层使用的技术

2. **文件定位**：回答以下问题，哪个文件负责什么？
   - `app.ts` 的作用是什么？
   - `authRoutes.ts` 和 `authMiddleware.ts` 的区别是什么？
   - `workflowExecutor.ts` 和 `workflowRoutes.ts` 的关系是什么？

3. **请求链路追踪**：当用户在前端点击"获取 Agent 列表"按钮时，描述从前端到后端再返回的完整链路

### 进阶练习

1. **模块依赖图**：画出 `llmService.ts` 的依赖关系图，列出所有依赖它和它依赖的模块

2. **启动流程分析**：在 `app.ts` 中，如果将 `initializeDatabase()` 移到服务初始化之后，会发生什么问题？为什么？

3. **添加新路由**：创建一个新的路由文件 `healthRoutes.ts`，在 `/api/health/details` 路径上返回系统健康详情，并在 `app.ts` 中注册

### 思考题

1. 为什么项目选择单体架构而非微服务？在什么情况下你会考虑将这个项目改造为微服务？

2. 项目的中间件注册顺序是否可以随意调整？如果调整了会产生什么影响？

3. 为什么后端使用同步的 better-sqlite3 而非异步的 node-sqlite3？如果未来并发量增加，应该如何应对？

---

## 延伸阅读

- [Express 中间件指南](https://expressjs.com/en/guide/writing-middleware.html)
- [React Router v6 文档](https://reactrouter.com/en/main)
- [better-sqlite3 文档](https://github.com/WiseLibs/better-sqlite3)
- [Socket.io 服务端文档](https://socket.io/docs/v4/server-api/)
- [优雅关闭的最佳实践](https://www.cloudbees.com/blog/graceful-shutdown-with-nodejs)

---

> **本章回顾**：你已经理解了项目的整体架构！在下一章中，我们将从零开始编写后端代码，深入实践后端开发的各个环节。
