it0/docs/web-admin-guide.md

1985 lines
111 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# IT0 PC Web 管理前端开发指导文档
> IT Operations Intelligent Agent — PC Web 管理控制台React + Next.js + Zustand/Redux Toolkit
## 1. 项目概述
### 1.1 定位
IT0 Web Admin 是**管理员级别的 PC 端控制台**,与 Flutter App移动端运维驾驶舱互补
| 维度 | Flutter App移动端 | Web AdminPC 端) |
|------|---------------------|-------------------|
| 角色 | 一线运维操作 | 管理配置 + 深度分析 |
| 场景 | 随时随地审批、巡检、对话 | 坐在电脑前做系统配置 |
| 核心功能 | AI 对话、快捷任务、审批、告警 | Agent 配置、Runbook 管理、权限控制、审计回溯 |
| 数据展示 | 简洁仪表盘 | 复杂表格、多维图表、终端全屏 |
### 1.2 技术栈
| 层面 | 技术选型 |
|------|---------|
| 框架 | Next.js 14+ (App Router) |
| 语言 | TypeScript (strict mode) |
| 状态管理 | Zustand轻量级 UI 状态) + Redux Toolkit复杂业务状态 |
| 样式 | Tailwind CSS + shadcn/ui 组件库 |
| 数据请求 | TanStack Query (React Query) v5 |
| 表单 | React Hook Form + Zod |
| 表格 | TanStack Table v8 |
| 图表 | Recharts / Tremor |
| 终端 | xterm.js |
| 代码编辑器 | Monaco Editor (Runbook/Prompt 编辑) |
| WebSocket | 原生 WebSocket + 自动重连 |
| 测试 | Vitest + React Testing Library |
| Lint | ESLint + Prettier |
### 1.3 设计原则
- **Clean Architecture**严格分层domain → application → infrastructure → presentation
- **Zustand + Redux Toolkit 混合**Zustand 管理 UI/交互状态侧边栏、模态框、主题Redux Toolkit 管理复杂业务状态Agent 配置、Runbook 编辑、权限矩阵)
- **Server Components 优先**Next.js App Router 的 RSC 用于静态/数据密集型页面Client Components 用于交互
- **管理员体验**:大屏优化、键盘快捷键、批量操作、深度搜索
- **多租户感知**:顶栏租户选择器 + 所有 API 请求自动携带 `X-Tenant-Id`,超级管理员可切换租户视角
---
## 2. 项目结构
### 2.1 整体目录
```
it0-web-admin/
├── src/
│ ├── app/ # Next.js App Router路由层
│ │ ├── layout.tsx # 根布局Providers、主题
│ │ ├── page.tsx # 首页(重定向到 dashboard
│ │ ├── (auth)/
│ │ │ ├── login/page.tsx
│ │ │ └── layout.tsx
│ │ ├── (admin)/ # 管理后台布局组
│ │ │ ├── layout.tsx # 侧边栏 + 顶栏布局
│ │ │ ├── dashboard/page.tsx
│ │ │ ├── agent-config/ # Agent 配置
│ │ │ │ ├── page.tsx # 引擎管理
│ │ │ │ ├── prompts/page.tsx # System Prompt 管理
│ │ │ │ ├── hooks/page.tsx # Hook 脚本管理
│ │ │ │ └── skills/ # ★ Claude Skills 管理
│ │ │ │ ├── page.tsx # Skill 列表
│ │ │ │ ├── [name]/page.tsx # Skill 详情/编辑
│ │ │ │ └── new/page.tsx # 新建 Skill
│ │ │ ├── runbooks/ # Runbook 管理
│ │ │ │ ├── page.tsx # 列表
│ │ │ │ ├── [id]/page.tsx # 详情/编辑
│ │ │ │ └── new/page.tsx # 新建
│ │ │ ├── standing-orders/ # ★ 驻留指令管理
│ │ │ │ ├── page.tsx # 指令列表 + 执行概况
│ │ │ │ ├── [id]/page.tsx # 指令详情/编辑/执行历史
│ │ │ │ └── [id]/executions/page.tsx # 执行记录详情
│ │ │ ├── servers/ # 服务器管理
│ │ │ │ ├── page.tsx
│ │ │ │ ├── [id]/page.tsx
│ │ │ │ └── clusters/page.tsx
│ │ │ ├── security/ # 安全配置
│ │ │ │ ├── risk-rules/page.tsx # 风险规则管理
│ │ │ │ ├── credentials/page.tsx # 凭证管理
│ │ │ │ └── permissions/page.tsx # RBAC 权限管理
│ │ │ ├── monitoring/ # 监控配置
│ │ │ │ ├── alert-rules/page.tsx # 告警规则
│ │ │ │ ├── health-checks/page.tsx # 健康检查配置
│ │ │ │ └── metrics/page.tsx # 指标面板
│ │ │ ├── communication/ # 通信配置
│ │ │ │ ├── channels/page.tsx # 渠道管理
│ │ │ │ ├── contacts/page.tsx # 联系人管理
│ │ │ │ └── escalation/page.tsx # 升级策略
│ │ │ ├── audit/ # 审计日志
│ │ │ │ └── page.tsx
│ │ │ ├── sessions/ # 会话历史
│ │ │ │ ├── page.tsx
│ │ │ │ └── [id]/page.tsx # 会话回放
│ │ │ ├── tenants/ # ★ 多租户管理(超级管理员)
│ │ │ │ ├── page.tsx # 租户列表
│ │ │ │ ├── [id]/page.tsx # 租户详情/编辑
│ │ │ │ └── new/page.tsx # 新建租户
│ │ │ └── terminal/ # Web 终端
│ │ │ └── page.tsx
│ │ └── api/ # Next.js API RoutesBFF 层)
│ │ ├── auth/[...nextauth]/route.ts
│ │ └── proxy/[...path]/route.ts # 反向代理到后端微服务
│ │
│ ├── core/ # 核心基础设施
│ │ ├── config/
│ │ │ └── app-config.ts # 环境配置
│ │ ├── api/
│ │ │ ├── api-client.ts # Fetch 封装token 注入)
│ │ │ ├── websocket-client.ts # WebSocket 管理
│ │ │ └── query-keys.ts # TanStack Query key 管理
│ │ ├── auth/
│ │ │ ├── auth-provider.tsx # 认证上下文
│ │ │ └── use-auth.ts # 认证 hook
│ │ ├── theme/
│ │ │ └── theme-config.ts
│ │ └── utils/
│ │ ├── date.ts
│ │ ├── format.ts
│ │ └── cn.ts # Tailwind className 合并
│ │
│ ├── domain/ # 领域层(纯 TypeScript零框架依赖
│ │ ├── entities/
│ │ │ ├── agent-engine.ts
│ │ │ ├── skill.ts # ★ Claude Skill 实体
│ │ │ ├── server.ts
│ │ │ ├── runbook.ts
│ │ │ ├── alert-rule.ts
│ │ │ ├── risk-rule.ts
│ │ │ ├── credential.ts
│ │ │ ├── user.ts
│ │ │ ├── role.ts
│ │ │ ├── audit-log.ts
│ │ │ ├── session.ts
│ │ │ ├── contact.ts
│ │ │ ├── escalation-policy.ts
│ │ │ ├── tenant.ts # ★ 租户实体
│ │ │ └── standing-order.ts # ★ 驻留指令实体
│ │ ├── value-objects/
│ │ │ ├── engine-type.ts
│ │ │ ├── risk-level.ts
│ │ │ ├── server-environment.ts
│ │ │ ├── alert-severity.ts
│ │ │ └── channel-type.ts
│ │ └── repositories/ # 仓储接口(抽象)
│ │ ├── agent-config.repository.ts
│ │ ├── server.repository.ts
│ │ ├── runbook.repository.ts
│ │ ├── alert-rule.repository.ts
│ │ ├── security.repository.ts
│ │ ├── audit.repository.ts
│ │ └── communication.repository.ts
│ │
│ ├── application/ # 应用层(用例)
│ │ ├── use-cases/
│ │ │ ├── agent-config/
│ │ │ │ ├── switch-engine.ts
│ │ │ │ ├── update-system-prompt.ts
│ │ │ │ └── manage-hooks.ts
│ │ │ ├── runbooks/
│ │ │ │ ├── create-runbook.ts
│ │ │ │ ├── update-runbook.ts
│ │ │ │ ├── delete-runbook.ts
│ │ │ │ └── test-runbook.ts
│ │ │ ├── servers/
│ │ │ │ ├── add-server.ts
│ │ │ │ ├── update-server.ts
│ │ │ │ └── remove-server.ts
│ │ │ ├── security/
│ │ │ │ ├── manage-risk-rules.ts
│ │ │ │ ├── manage-credentials.ts
│ │ │ │ └── manage-permissions.ts
│ │ │ └── monitoring/
│ │ │ ├── create-alert-rule.ts
│ │ │ └── configure-health-check.ts
│ │ └── dto/
│ │ ├── agent-config.dto.ts
│ │ ├── runbook.dto.ts
│ │ └── server.dto.ts
│ │
│ ├── infrastructure/ # 基础设施层(适配器实现)
│ │ └── repositories/
│ │ ├── api-agent-config.repository.ts
│ │ ├── api-server.repository.ts
│ │ ├── api-runbook.repository.ts
│ │ ├── api-alert-rule.repository.ts
│ │ ├── api-security.repository.ts
│ │ ├── api-audit.repository.ts
│ │ ├── api-communication.repository.ts
│ │ └── api-tenant.repository.ts # ★ 租户管理 API
│ │
│ ├── stores/ # 状态管理
│ │ ├── zustand/ # Zustand — UI/交互状态
│ │ │ ├── ui-store.ts # 侧边栏、模态框、面包屑
│ │ │ ├── theme-store.ts # 主题切换
│ │ │ ├── terminal-store.ts # 终端会话状态
│ │ │ ├── notification-store.ts # Toast 通知队列
│ │ │ └── tenant-store.ts # ★ 当前租户上下文选中的租户ID
│ │ └── redux/ # Redux Toolkit — 复杂业务状态
│ │ ├── store.ts # Redux store 配置
│ │ ├── slices/
│ │ │ ├── agent-config.slice.ts # Agent 引擎配置状态
│ │ │ ├── runbook-editor.slice.ts # Runbook 编辑器状态(复杂表单+版本控制)
│ │ │ ├── permission-matrix.slice.ts # RBAC 权限矩阵(多维度交叉编辑)
│ │ │ ├── risk-rules.slice.ts # 风险规则集管理
│ │ │ └── session-replay.slice.ts # 会话回放状态(时间线+步骤)
│ │ └── middleware/
│ │ └── audit-middleware.ts # 配置变更自动记录审计
│ │
│ ├── components/ # 共享 UI 组件
│ │ ├── layout/
│ │ │ ├── sidebar.tsx # 侧边导航
│ │ │ ├── header.tsx # 顶栏(搜索、通知、用户菜单、★ 租户选择器)
│ │ │ ├── breadcrumb.tsx
│ │ │ └── main-layout.tsx
│ │ ├── ui/ # shadcn/ui 基础组件
│ │ │ ├── button.tsx
│ │ │ ├── input.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── data-table.tsx # TanStack Table 封装
│ │ │ ├── command.tsx # 命令面板 (Cmd+K)
│ │ │ ├── toast.tsx
│ │ │ └── ...
│ │ └── domain/ # 业务组件
│ │ ├── server-status-badge.tsx
│ │ ├── risk-level-tag.tsx
│ │ ├── engine-type-badge.tsx
│ │ ├── alert-severity-icon.tsx
│ │ ├── command-syntax-highlight.tsx # Bash 命令高亮
│ │ ├── json-viewer.tsx # JSON 树形查看器
│ │ └── metrics-chart.tsx # 指标图表组件
│ │
│ └── hooks/ # 自定义 Hooks
│ ├── use-websocket.ts # WebSocket 连接管理
│ ├── use-debounce.ts
│ ├── use-keyboard-shortcut.ts # 键盘快捷键
│ └── use-confirmation.ts # 危险操作确认弹窗
├── public/
├── next.config.ts
├── tailwind.config.ts
├── tsconfig.json
├── package.json
└── vitest.config.ts
```
---
## 3. Clean Architecture 分层规则
```
┌─────────────────────────────────────────┐
│ Presentation Layer │
│ (app/ pages, components/, hooks/) │
│ - Next.js 页面 & React 组件 │
│ - 只能调用 application 层的 use-cases │
│ - 通过 Zustand/Redux 管理 UI 状态 │
│ - 通过 TanStack Query 管理服务端状态 │
└────────────────┬────────────────────────┘
│ 调用
┌────────────────▼────────────────────────┐
│ Application Layer │
│ (application/use-cases/) │
│ - 用例编排(业务流程) │
│ - 调用 domain 层 repository 接口 │
│ - DTO 转换 │
└────────────────┬────────────────────────┘
│ 依赖倒置
┌────────────────▼────────────────────────┐
│ Domain Layer │
│ (domain/entities, value-objects, │
│ repositories 接口) │
│ - 纯 TypeScript零框架依赖 │
│ - Entity 定义、业务规则 │
│ - Repository 接口(抽象类/interface
└────────────────△────────────────────────┘
│ 实现
┌────────────────┴────────────────────────┐
│ Infrastructure Layer │
│ (infrastructure/repositories/) │
│ - Repository 接口的具体实现 │
│ - 调用后端 API (fetch/axios) │
│ - 数据模型 ↔ 领域实体转换 │
└─────────────────────────────────────────┘
```
**依赖方向**`presentation → application → domain ← infrastructure`
---
## 4. 状态管理策略Zustand + Redux Toolkit 混合
### 4.1 分工原则
```
┌────────────────────────────────────────────────────────┐
│ Zustand │
│ 管理「轻量级 UI 状态」— 直接、简单、无样板代码 │
│ │
│ • 侧边栏展开/折叠 │
│ • 模态框开关 │
│ • 当前主题(暗色/亮色) │
│ • Toast 通知队列 │
│ • 终端会话(当前标签页、连接状态) │
│ • 命令面板Cmd+K开关 │
│ • 面包屑路径 │
│ │
│ 特点:一个 store = 一个 create(),直接 get/set │
└────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ Redux Toolkit │
│ 管理「复杂业务状态」— 可预测、可追溯、中间件生态 │
│ │
│ • Agent 配置管理 │
│ (引擎选择、参数配置、提示词版本控制) │
│ • Runbook 编辑器 │
│ (多步骤表单、版本对比、草稿保存、撤销/重做) │
│ • RBAC 权限矩阵 │
│ (角色×权限×资源 三维编辑、批量操作、变更对比) │
│ • 风险规则集管理 │
│ (规则排序、优先级、条件组合、测试模拟) │
│ • 会话回放 │
│ (时间线导航、步骤切换、工具执行展开/折叠) │
│ │
│ 特点slice + createAsyncThunk + middleware │
│ 优势DevTools 调试、时间旅行、审计中间件 │
└────────────────────────────────────────────────────────┘
┌────────────────────────────────────────────────────────┐
│ TanStack Query │
│ 管理「服务端状态」— 缓存、失效、重新获取 │
│ │
│ • 服务器列表、详情 │
│ • 告警规则列表 │
│ • 审计日志查询 │
│ • 联系人列表 │
│ • 所有 GET 请求的缓存管理 │
│ │
│ 特点:声明式数据获取,自动缓存失效 │
└────────────────────────────────────────────────────────┘
```
### 4.2 Zustand Store 示例
```typescript
// stores/zustand/ui-store.ts
interface UiState {
sidebarCollapsed: boolean;
commandPaletteOpen: boolean;
activeModal: string | null;
breadcrumbs: BreadcrumbItem[];
}
interface UiActions {
toggleSidebar: () => void;
openCommandPalette: () => void;
closeCommandPalette: () => void;
openModal: (id: string) => void;
closeModal: () => void;
setBreadcrumbs: (items: BreadcrumbItem[]) => void;
}
export const useUiStore = create<UiState & UiActions>((set) => ({
// State
sidebarCollapsed: false,
commandPaletteOpen: false,
activeModal: null,
breadcrumbs: [],
// Actions
toggleSidebar: () => set((s) => ({ sidebarCollapsed: !s.sidebarCollapsed })),
openCommandPalette: () => set({ commandPaletteOpen: true }),
closeCommandPalette: () => set({ commandPaletteOpen: false }),
openModal: (id) => set({ activeModal: id }),
closeModal: () => set({ activeModal: null }),
setBreadcrumbs: (items) => set({ breadcrumbs: items }),
}));
```
```typescript
// stores/zustand/theme-store.ts
type Theme = 'light' | 'dark' | 'system';
interface ThemeState {
theme: Theme;
setTheme: (theme: Theme) => void;
}
export const useThemeStore = create<ThemeState>()(
persist(
(set) => ({
theme: 'dark',
setTheme: (theme) => set({ theme }),
}),
{ name: 'it0-theme' },
),
);
```
### 4.3 Redux Toolkit Slice 示例
```typescript
// stores/redux/slices/agent-config.slice.ts
interface AgentConfigState {
activeEngine: EngineType;
engines: Record<EngineType, EngineConfig>;
systemPromptVersions: SystemPromptVersion[];
currentPromptDraft: string | null;
hookScripts: HookScript[];
isLoading: boolean;
error: string | null;
// 变更追踪
unsavedChanges: boolean;
lastSavedAt: string | null;
}
const initialState: AgentConfigState = {
activeEngine: 'claude_code_cli',
engines: {
claude_code_cli: { path: '/usr/local/bin/claude', maxTurns: 30, maxBudgetUsd: 5 },
claude_api: { model: 'claude-sonnet-4-5-20250929', apiKey: '', maxTokens: 8192 },
custom: { endpoint: '', authToken: '' },
},
systemPromptVersions: [],
currentPromptDraft: null,
hookScripts: [],
isLoading: false,
error: null,
unsavedChanges: false,
lastSavedAt: null,
};
export const agentConfigSlice = createSlice({
name: 'agentConfig',
initialState,
reducers: {
setActiveEngine: (state, action: PayloadAction<EngineType>) => {
state.activeEngine = action.payload;
state.unsavedChanges = true;
},
updateEngineConfig: (state, action: PayloadAction<{ type: EngineType; config: Partial<EngineConfig> }>) => {
state.engines[action.payload.type] = {
...state.engines[action.payload.type],
...action.payload.config,
};
state.unsavedChanges = true;
},
setPromptDraft: (state, action: PayloadAction<string>) => {
state.currentPromptDraft = action.payload;
state.unsavedChanges = true;
},
addHookScript: (state, action: PayloadAction<HookScript>) => {
state.hookScripts.push(action.payload);
state.unsavedChanges = true;
},
removeHookScript: (state, action: PayloadAction<string>) => {
state.hookScripts = state.hookScripts.filter(h => h.id !== action.payload);
state.unsavedChanges = true;
},
markSaved: (state) => {
state.unsavedChanges = false;
state.lastSavedAt = new Date().toISOString();
},
},
extraReducers: (builder) => {
builder
.addCase(fetchAgentConfig.pending, (state) => {
state.isLoading = true;
state.error = null;
})
.addCase(fetchAgentConfig.fulfilled, (state, action) => {
state.isLoading = false;
Object.assign(state, action.payload);
state.unsavedChanges = false;
})
.addCase(fetchAgentConfig.rejected, (state, action) => {
state.isLoading = false;
state.error = action.error.message ?? 'Failed to load config';
})
.addCase(saveAgentConfig.fulfilled, (state) => {
state.unsavedChanges = false;
state.lastSavedAt = new Date().toISOString();
});
},
});
// Async Thunks
export const fetchAgentConfig = createAsyncThunk(
'agentConfig/fetch',
async (_, { extra }) => {
const repo = extra as { agentConfigRepo: AgentConfigRepository };
return repo.agentConfigRepo.getConfig();
},
);
export const saveAgentConfig = createAsyncThunk(
'agentConfig/save',
async (_, { getState, extra }) => {
const state = (getState() as RootState).agentConfig;
const repo = extra as { agentConfigRepo: AgentConfigRepository };
return repo.agentConfigRepo.saveConfig({
activeEngine: state.activeEngine,
engines: state.engines,
hookScripts: state.hookScripts,
});
},
);
export const { setActiveEngine, updateEngineConfig, setPromptDraft, addHookScript, removeHookScript, markSaved } =
agentConfigSlice.actions;
```
```typescript
// stores/redux/slices/runbook-editor.slice.ts
interface RunbookEditorState {
currentRunbook: Runbook | null;
draft: Partial<Runbook>;
originalSnapshot: string | null; // JSON snapshot 用于变更对比
validationErrors: Record<string, string>;
testResult: RunbookTestResult | null;
isTesting: boolean;
history: RunbookHistoryEntry[]; // 撤销/重做栈
historyIndex: number;
}
const runbookEditorSlice = createSlice({
name: 'runbookEditor',
initialState: { /* ... */ } as RunbookEditorState,
reducers: {
loadRunbook: (state, action: PayloadAction<Runbook>) => {
state.currentRunbook = action.payload;
state.draft = { ...action.payload };
state.originalSnapshot = JSON.stringify(action.payload);
state.validationErrors = {};
state.history = [];
state.historyIndex = -1;
},
updateDraftField: (state, action: PayloadAction<{ field: string; value: unknown }>) => {
const { field, value } = action.payload;
// 保存历史(撤销支持)
state.history = state.history.slice(0, state.historyIndex + 1);
state.history.push({ field, oldValue: (state.draft as any)[field], newValue: value });
state.historyIndex = state.history.length - 1;
// 更新值
(state.draft as any)[field] = value;
},
undo: (state) => {
if (state.historyIndex < 0) return;
const entry = state.history[state.historyIndex];
(state.draft as any)[entry.field] = entry.oldValue;
state.historyIndex--;
},
redo: (state) => {
if (state.historyIndex >= state.history.length - 1) return;
state.historyIndex++;
const entry = state.history[state.historyIndex];
(state.draft as any)[entry.field] = entry.newValue;
},
setValidationErrors: (state, action: PayloadAction<Record<string, string>>) => {
state.validationErrors = action.payload;
},
clearEditor: (state) => {
state.currentRunbook = null;
state.draft = {};
state.originalSnapshot = null;
state.history = [];
state.historyIndex = -1;
},
},
extraReducers: (builder) => {
builder
.addCase(testRunbook.pending, (state) => { state.isTesting = true; })
.addCase(testRunbook.fulfilled, (state, action) => {
state.isTesting = false;
state.testResult = action.payload;
})
.addCase(testRunbook.rejected, (state) => { state.isTesting = false; });
},
});
```
### 4.4 Redux Store 配置
```typescript
// stores/redux/store.ts
import { configureStore } from '@reduxjs/toolkit';
import { agentConfigSlice } from './slices/agent-config.slice';
import { runbookEditorSlice } from './slices/runbook-editor.slice';
import { permissionMatrixSlice } from './slices/permission-matrix.slice';
import { riskRulesSlice } from './slices/risk-rules.slice';
import { sessionReplaySlice } from './slices/session-replay.slice';
import { auditMiddleware } from './middleware/audit-middleware';
// 依赖注入:通过 extra 参数注入 repository 实现
export const createStore = (repositories: Repositories) =>
configureStore({
reducer: {
agentConfig: agentConfigSlice.reducer,
runbookEditor: runbookEditorSlice.reducer,
permissionMatrix: permissionMatrixSlice.reducer,
riskRules: riskRulesSlice.reducer,
sessionReplay: sessionReplaySlice.reducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
thunk: { extraArgument: repositories },
serializableCheck: {
ignoredActions: ['sessionReplay/loadSession'],
},
}).concat(auditMiddleware),
});
export type RootState = ReturnType<ReturnType<typeof createStore>['getState']>;
export type AppDispatch = ReturnType<typeof createStore>['dispatch'];
```
### 4.5 审计中间件(配置变更自动追踪)
```typescript
// stores/redux/middleware/audit-middleware.ts
import { Middleware } from '@reduxjs/toolkit';
/**
* 审计中间件:
* 拦截所有「配置变更」类 action自动向 audit-service 发送记录
* 这是使用 Redux 的核心价值之一 — 所有状态变更经过统一管道
*/
export const auditMiddleware: Middleware = (store) => (next) => (action) => {
const result = next(action);
// 只追踪配置变更类 action
const auditableActions = [
'agentConfig/setActiveEngine',
'agentConfig/updateEngineConfig',
'agentConfig/saveAgentConfig/fulfilled',
'riskRules/addRule',
'riskRules/removeRule',
'riskRules/updateRule',
'permissionMatrix/updatePermission',
];
if (auditableActions.includes(action.type)) {
// 异步发送审计记录(不阻塞 UI
fetch('/api/proxy/audit-service/logs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
actionType: 'config_change',
actorType: 'user',
detail: {
action: action.type,
payload: action.payload,
timestamp: new Date().toISOString(),
},
}),
}).catch(console.error);
}
return result;
};
```
---
## 5. 核心页面详细设计
### 5.1 Agent 配置页(核心)
这是 Web Admin 最重要的页面 — 配置 AI Agent 的行为。
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Agent Configuration │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 引擎选择 ──────────────────────────────────────────── │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ● Claude Code │ │ ○ Claude API │ │ ○ Custom Agent │ │
│ │ CLI │ │ │ │ (Coming Soon) │ │
│ │ Active ✓ │ │ │ │ │ │
│ └─────────────────┘ └─────────────────┘ └─────────────────┘ │
│ │
│ ── Claude Code CLI 配置 ──────────────────────────────── │
│ │
│ CLI 路径: [/usr/local/bin/claude ] │
│ 最大轮次: [30 ] │
│ 预算上限: [$5.00 ] / 每次任务 │
│ 允许工具: [✓ Bash] [✓ Read] [✓ Grep] [✓ Glob] [□ Write] │
│ │
│ ── System Prompt 管理 ────────────────────────────────── │
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ [Monaco Editor] │ │
│ │ │ │
│ │ 你是 IT0 运维智能体,负责管理以下服务器集群... │ │
│ │ │ │
│ │ ## 操作规范 │ │
│ │ ### 权限分级 │ │
│ │ - Level 0自动执行... │ │
│ │ │ │
│ └────────────────────────────────────────────────────────┘ │
│ 版本: v3 (2026-02-08) [变更历史] [还原上一版] [保存新版本] │
│ │
│ ── Hook 脚本管理 ─────────────────────────────────────── │
│ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ PreToolUse │ Bash │ validate.py [编辑][删除] │ │
│ │ PostToolUse │ * │ audit-logger.py [编辑][删除] │ │
│ └──────────────────────────────────────────────────┘ │
│ [+ 添加 Hook 脚本] │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ [有未保存的更改] [取消] [保存配置] │
└──────────────────────────────────────────────────────────────────┘
```
### 5.2 Runbook 管理页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Runbooks [+ 新建 Runbook] │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 搜索: [________________] 筛选: [全部类型 ▼] [全部状态 ▼] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 名称 │ 触发方式 │ 最高风险│ 自动审批│ 状态 │ │
│ ├────────────────────┼──────────┼─────────┼────────┼───────┤ │
│ │ 全面健康检查 │ 手动/定时 │ L0 │ ✓ │ 启用 │ │
│ │ 磁盘空间清理 │ 告警触发 │ L1 │ ✓ │ 启用 │ │
│ │ 应用滚动部署 │ 手动 │ L2 │ ✗ │ 启用 │ │
│ │ 数据库主从切换 │ 手动 │ L2 │ ✗ │ 禁用 │ │
│ │ 紧急回滚 │ 手动 │ L2 │ ✗ │ 启用 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 显示 1-5 共 5 条 │
└──────────────────────────────────────────────────────────────────┘
```
#### Runbook 编辑页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Runbooks > 磁盘空间清理 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 名称: [磁盘空间清理 ] │
│ 描述: [当磁盘使用率超过阈值时自动清理旧日志文件 ] │
│ │
│ 触发方式: ○ 手动 ● 告警触发 ○ 定时 │
│ 关联告警: [disk_usage_high ▼] │
│ │
│ 最高风险等级: [L1 ▼] │
│ 自动审批: [✓] (仅 Level 0-1 的命令自动通过) │
│ 允许工具: [✓ Bash] [✓ Read] [✓ Grep] [□ Write] │
│ │
│ ── Prompt 模板 ──────────────────────────────────────── │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ [Monaco Editor] │ │
│ │ │ │
│ │ 请检查以下服务器的磁盘使用情况: │ │
│ │ {{target_servers}} │ │
│ │ │ │
│ │ 执行步骤: │ │
│ │ 1. 检查 df -h 输出,找到使用率 > 80% 的分区 │ │
│ │ 2. 查看 /var/log 下的大文件 │ │
│ │ 3. 清理 30 天前的日志文件 │ │
│ │ 4. 验证清理后的磁盘使用率 │ │
│ │ 5. 汇报清理结果和释放的空间 │ │
│ │ │ │
│ └──────────────────────────────────────────────────────┘ │
│ 模板变量: {{target_servers}} {{alert_message}} {{threshold}} │
│ │
│ ── 测试 ─────────────────────────────────────────────── │
│ 目标服务器: [prod-1, prod-2 ▼] │
│ [模拟测试Dry Run] [实际测试] │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ [Ctrl+Z 撤销] [Ctrl+Y 重做] [取消] [保存草稿] [发布] │
└──────────────────────────────────────────────────────────────────┘
```
### 5.3 ★ 驻留指令管理Standing Orders
#### 驻留指令列表页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Standing Orders │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 驻留指令 — Agent 自治执行的任务定义 │
│ (通过 Flutter App 对话创建,此处管理和监控) │
│ │
│ 状态: [全部 ▾] 触发: [全部 ▾] [搜索...] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 指令ID │ 名称 │ 触发 │ 上次执行 │ 下次 │ 状态 │
│ ├─────────────────┼────────────────┼──────────┼───────────┼──────┼────────┤
│ │ SO-20260208-001 │ 每日生产巡检 │ ⏰ 08:00 │ 今天 08:00│ 明天 │ ✓ 活跃 │
│ │ │ │ (每日) │ ✓ 正常 │08:00 │ │
│ │ SO-20260208-002 │ 磁盘空间监控 │ ⚡ 事件 │ 1h 前 │ — │ ✓ 活跃 │
│ │ │ │ (告警触发)│ ⚠ 已升级 │ │ │
│ │ SO-20260210-001 │ 周末日志归档 │ ⏰ 02:00 │ 上周六 │ 本周 │ ✓ 活跃 │
│ │ │ │ (每周六) │ ✓ 正常 │ 六 │ │
│ │ SO-20260205-001 │ 部署后烟雾测试 │ ⚡ 事件 │ 2天前 │ — │ ⏸ 暂停 │
│ │ │ │ (部署后) │ ✓ 正常 │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 共 4 条指令 · 活跃 3 · 暂停 1 │
│ 今日已执行 5 次 · 成功 4 · 升级 1 │
│ │
└──────────────────────────────────────────────────────────────────┘
```
#### 驻留指令详情页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Standing Orders > 每日生产巡检 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 基本信息 ────────────────────────────────────────────────── │
│ ID: SO-20260208-001 │
│ 名称: 每日生产巡检 │
│ 状态: ● 活跃 [⏸ 暂停] [🗑 归档] │
│ 创建方式: 对话定义(会话 sess-abc-123 [查看原始对话] │
│ 最后修改: 对话修改(会话 sess-def-456 [查看修改对话] │
│ │
│ ── 触发配置 ────────────────────────────────────────────────── │
│ 触发方式: [⏰ 定时 (Cron) ▾] │
│ Cron 表达式: [0 8 * * * ] → 每天 08:00 │
│ 生效时间: 2026-02-09 起 │
│ 过期时间: [无 (永久) ▾] │
│ │
│ ── 目标范围 ────────────────────────────────────────────────── │
│ ☑ 按环境筛选: [prod ▾] │
│ ☐ 指定服务器 ☐ 指定集群 ☐ 所有服务器 │
│ 匹配服务器: prod-1, prod-2, prod-3 (共 3 台) │
│ │
│ ── Agent 执行指令 ──────────────────────────────────────────── │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ (Monaco Editor — 可编辑 Agent Prompt) │ │
│ │ │ │
│ │ 检查所有目标服务器的 CPU、内存、磁盘使用率。 │ │
│ │ 如果磁盘使用率超过 80%,清理 7 天前的日志文件。 │ │
│ │ 汇总所有指标,生成巡检报告。 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ 关联 Skills: [cleanup_logs] [inspect] │
│ 关联 Runbook: (无) │
│ 最高风险等级: [L1 — 低风险写入 ▾] │
│ 最大轮次: [20] 预算上限: [$0.50] │
│ │
│ ── 决策边界 ────────────────────────────────────────────────── │
│ ✅ 允许的操作: │
│ ┌────────────────────────────────────────────────────┐ │
│ │ cleanup_logs (清理日志) [✕ 移除]│ │
│ │ report_metrics (汇报指标) [✕ 移除]│ │
│ │ [+ 添加操作] │ │
│ └────────────────────────────────────────────────────┘ │
│ │
│ 🚨 升级规则: │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 条件 │ 渠道 │ 优先级 │ 操作 │ │
│ ├─────────────────────────┼─────────────┼───────────┼────────┤ │
│ │ disk_usage > 95% │ 🔔 推送+电话│ 🔴 紧急 │ [编辑] │ │
│ │ cpu > 90% for 10min │ 🔔 推送+电话│ 🔴 紧急 │ [编辑] │ │
│ │ other_anomaly │ 📱 推送+短信│ 🟡 普通 │ [编辑] │ │
│ │ [+ 添加规则] │ │ │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ── 执行历史 ────────────────────────────────────────────────── │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 时间 │ 状态 │ 耗时 │ 操作 │ 详情 │ │
│ ├───────────────────┼──────────┼───────┼──────────────┼──────┤ │
│ │ 2026-02-08 08:00 │ ✓ 完成 │ 45s │ 清理 3.2GB │ [▶] │ │
│ │ 2026-02-07 08:00 │ ✓ 完成 │ 38s │ 全部正常 │ [▶] │ │
│ │ 2026-02-06 08:00 │ ⚠ 已升级 │ 12min │ 磁盘 97% → │ [▶] │ │
│ │ │ │ │ 电话通知 │ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ [查看全部执行记录] │
│ │
│ ─────────────────────────────────────────────────────────── │
│ [取消] [保存修改] │
│ │
└──────────────────────────────────────────────────────────────────┘
```
> **注意**:驻留指令主要通过 **Flutter App 对话** 创建(更自然的交互方式)。
> Web Admin 侧重于**管理、监控和微调**已有的指令,提供更精确的配置编辑能力。
---
### 5.4 服务器管理 — 添加/编辑/凭证/跳板机
#### 服务器列表页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Servers [+ 添加服务器] │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 搜索: [________________] 环境: [全部 ▼] 角色: [全部 ▼] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 名称 │ IP/主机 │环境 │角色 │网络 │跳板 │状态 │ │
│ ├──────────┼──────────────┼─────┼──────┼──────┼─────┼──────┤ │
│ │ jump-01 │ 203.0.113.10 │prod │gateway│公网 │ — │● 在线│ │
│ │ prod-1 │ 10.0.1.11 │prod │web │内网 │jump-01│● 在线│ │
│ │ prod-2 │ 10.0.1.12 │prod │web │内网 │jump-01│● 在线│ │
│ │ prod-db │ 10.0.1.20 │prod │db │内网 │jump-01│● 在线│ │
│ │ staging-1│ 10.0.2.11 │stg │web │内网 │jump-01│● 在线│ │
│ │ dev-1 │ 192.168.1.100│dev │web │公网 │ — │● 在线│ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [批量测试连接] [导出 SSH Config] │
└──────────────────────────────────────────────────────────────────┘
```
#### 添加/编辑服务器表单
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Servers > 添加服务器 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 基本信息 ──────────────────────────────────────────── │
│ │
│ 服务器名称: [prod-web-03 ] │
│ 描述: [生产环境 Web 服务器 #3 ] │
│ 环境: ○ dev ○ staging ● production │
│ 角色: [Web Server ▼] │
│ 所属集群: [main-cluster ▼] (可选) │
│ 标签: [region:us-east] [tier:1] [+ 添加] │
│ │
│ ── SSH 连接配置 ──────────────────────────────────────── │
│ │
│ 主机地址: [10.0.1.13 ] │
│ SSH 端口: [22 ] │
│ SSH 用户名: [ops-agent ] │
│ │
│ ── 认证方式 ──────────────────────────────────────────── │
│ │
│ ● SSH 密钥 ○ 密码 │
│ │
│ ┌─ SSH 密钥认证 ──────────────────────────────────────────┐ │
│ │ │ │
│ │ 选择已有凭证: [prod-ssh-key-ed25519 ▼] │ │
│ │ 指纹: SHA256:abc123... 类型: ed25519 │ │
│ │ │ │
│ │ 或上传新密钥: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 将私钥文件拖拽到此处,或 [点击上传] │ │ │
│ │ │ 支持: id_rsa, id_ed25519, id_ecdsa │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ 密钥名称: [prod-key-03 ] │ │
│ │ 密钥密码: [●●●●●●●● ] (如果密钥有 passphrase) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ┌─ 密码认证(不推荐,仅用于遗留系统)─────────────────────┐ │
│ │ │ │
│ │ 选择已有凭证: [选择... ▼] │ │
│ │ 或输入新密码: [●●●●●●●● ] │ │
│ │ 凭证名称: [ ] │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ── 网络拓扑 ──────────────────────────────────────────── │
│ │
│ 网络类型: ○ 公网(可直接连接) │
│ ● 内网(需要跳板机) │
│ │
│ ┌─ 跳板机配置SSH ProxyJump────────────────────────────┐ │
│ │ │ │
│ │ 跳板机: [jump-01 (203.0.113.10) ▼] │ │
│ │ 已配置的跳板机/网关服务器列表 │ │
│ │ │ │
│ │ 连接链路预览: │ │
│ │ ┌─────────────────────────────────────────────────┐ │ │
│ │ │ 你的管理服务器 │ │ │
│ │ │ ↓ SSH (203.0.113.10:22, user: jump-user) │ │ │
│ │ │ jump-01 (跳板机) │ │ │
│ │ │ ↓ SSH (10.0.1.13:22, user: ops-agent) │ │ │
│ │ │ prod-web-03 (目标服务器) ✓ │ │ │
│ │ └─────────────────────────────────────────────────┘ │ │
│ │ │ │
│ │ ⚠ 多级跳板: 如果跳板机本身也在内网,将自动递归构建 │ │
│ │ ProxyJump 链 (如 jump-01 → jump-02 → target) │ │
│ │ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
│ ── 高级 SSH 选项(可选)──────────────────────────────── │
│ │
│ [✓] StrictHostKeyChecking=no (首次连接不验证指纹) │
│ [ ] ServerAliveInterval=60 (保持连接) │
│ [ ] ServerAliveCountMax=3 │
│ 自定义选项: [ ] │
│ │
│ ── 连接测试 ──────────────────────────────────────────── │
│ │
│ [测试 SSH 连接] │
│ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ ✓ 跳板机 jump-01 连接成功 (延迟 23ms) │ │
│ │ ✓ 目标 prod-web-03 连接成功 (延迟 45ms) │ │
│ │ ✓ 用户 ops-agent 权限验证通过 │ │
│ │ ✓ 基本命令执行正常 (whoami, uname -a) │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ │
│ [取消] [保存服务器] │
└──────────────────────────────────────────────────────────────────┘
```
#### 凭证管理页Security > Credentials 的补充说明)
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Security > Credentials [+ 添加凭证] │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 名称 │ 类型 │ 指纹/信息 │ 引用 │ 操作 │ │
│ ├─────────────────┼──────────┼────────────────┼──────┼──────┤ │
│ │ prod-ssh-key │ ed25519 │ SHA256:abc1... │ 4台 │[编辑]│ │
│ │ staging-ssh-key │ rsa-4096 │ SHA256:def4... │ 2台 │[编辑]│ │
│ │ dev-password │ 密码 │ ●●●●●●●● │ 1台 │[编辑]│ │
│ │ jump-key │ ed25519 │ SHA256:ghi7... │ 1台 │[编辑]│ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ⚠ 凭证内容加密存储AES-256-GCM仅在 Agent 执行 SSH 时 │
│ 临时解密到内存,绝不写入日志或磁盘。 │
│ ⚠ 「引用」列显示使用该凭证的服务器数量,删除前需先解除关联。 │
│ │
└──────────────────────────────────────────────────────────────────┘
```
#### SSH 连接拓扑可视化
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Servers > Network Topology │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌───────────┐ │
│ │ IT0 管理机 │ │
│ └─────┬─────┘ │
│ │ │
│ ┌─────▼─────┐ │
│ │ jump-01 │ 203.0.113.10 (公网) │
│ │ (跳板机) │ │
│ └──┬──┬──┬──┘ │
│ ┌─────┘ │ └─────┐ │
│ ┌─────▼───┐┌───▼────┐┌──▼──────┐ │
│ │ prod-1 ││ prod-2 ││ prod-db │ 10.0.1.x (内网) │
│ │ 10.0.1.11│ 10.0.1.12│ 10.0.1.20│ │
│ └─────────┘└────────┘└─────────┘ │
│ │
│ ┌─────────┐ │
│ │ dev-1 │ 192.168.1.100 (公网直连) │
│ └─────────┘ │
│ │
│ 图例: ● 在线 ○ 离线 ⚠ 告警 │
└──────────────────────────────────────────────────────────────────┘
```
### 5.5 安全配置 — 风险规则管理
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Security > Risk Rules [+ 添加规则] │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 命令风险分级规则(按优先级排序,可拖拽排序)────────── │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ ⠿ │ Level 3 🟣│ rm\s+-rf\s+/ │ 禁止 │ [编辑] │ │
│ │ ⠿ │ Level 3 🟣│ DROP\s+(DATABASE|TABLE) │ 禁止 │ [编辑] │ │
│ │ ⠿ │ Level 3 🟣│ shutdown|reboot │ 禁止 │ [编辑] │ │
│ │ ⠿ │ Level 3 🟣│ mkfs|dd\s+if= │ 禁止 │ [编辑] │ │
│ │ ⠿ │ Level 2 🔴│ systemctl\s+(stop|restart) │ 审批│ [编辑] │ │
│ │ ⠿ │ Level 2 🔴│ kubectl\s+delete │ 审批 │ [编辑] │ │
│ │ ⠿ │ Level 2 🔴│ docker\s+(rm|stop) │ 审批 │ [编辑] │ │
│ │ ⠿ │ Level 1 🟠│ systemctl\s+restart │ 确认 │ [编辑] │ │
│ │ ⠿ │ Level 1 🟠│ find.*-delete │ 确认 │ [编辑] │ │
│ │ ⠿ │ Level 0 🟢│ *(默认) │ 自动 │ — │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ── 规则测试 ──────────────────────────────────────────── │
│ 输入命令: [ssh prod-1 'systemctl restart nginx' ] │
│ 结果: Level 2 🔴 — 匹配规则 "systemctl\s+(stop|restart)" │
│ 动作: 需要审批 │
│ │
└──────────────────────────────────────────────────────────────────┘
```
### 5.6 审计日志页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Audit Logs │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 时间范围: [2026-02-01] ~ [2026-02-08] │
│ 操作类型: [全部 ▼] 执行者: [全部 ▼] 搜索: [___________] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 时间 │ 操作类型 │ 执行者 │ 详情 │ │
│ ├────────────────┼───────────────┼──────────┼──────────────┤ │
│ │ 02-08 15:23:01 │ command_exec │ agent │ df -h (prod-1)│ │
│ │ 02-08 15:22:45 │ task_created │ admin │ 全面巡检 │ │
│ │ 02-08 14:15:32 │ approval_ok │ admin │ restart nginx │ │
│ │ 02-08 14:15:10 │ approval_req │ agent │ restart nginx │ │
│ │ 02-08 13:00:00 │ config_change │ admin │ 引擎切换→CLI │ │
│ │ 02-08 12:01:15 │ alert_fired │ system │ disk > 90% │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ [← 上一页] 第 1 页 / 共 12 页 [下一页 →] │
│ │
│ [导出 CSV] │
└──────────────────────────────────────────────────────────────────┘
```
### 5.7 会话回放页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Sessions > Session #a3f8c2 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ 任务: 全面巡检 引擎: Claude Code CLI 时间: 2026-02-08 15:22│
│ 状态: ✓ 完成 耗时: 3m 42s 命令数: 12 │
│ │
│ ── 执行时间线 ───────────────────────────────────────── │
│ │
│ ●──○──○──○──○──○──○──○──○──○──○──● │
│ ↑ ↑ │
│ 开始 完成 │
│ │
│ ── 步骤详情 ─────────────────────────────────────────── │
│ │
│ [Step 1] 🧠 思考 │
│ "我需要检查所有服务器的 CPU、内存、磁盘状态..." │
│ │
│ [Step 2] 🔧 Bash: ssh prod-1 'top -bn1 | head -5' L0 🟢 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ top - 15:22:35 up 45 days, 12:34, 2 users │ │
│ │ Tasks: 234 total, 1 running, 233 sleeping │ │
│ │ %Cpu(s): 45.2 us, 3.1 sy, 0.0 ni, 51.7 id │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ [Step 3] 🔧 Bash: ssh prod-1 'df -h' L0 🟢 │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ Filesystem Size Used Avail Use% Mounted on │ │
│ │ /dev/sda1 100G 71G 29G 71% / │ │
│ └──────────────────────────────────────────────────────┘ │
│ │
│ [Step 4] 🧠 思考 │
│ "prod-1 状态正常,继续检查 prod-2..." │
│ │
│ ... (可展开/折叠每一步) │
│ │
│ [Step 12] 📝 总结 │
│ "巡检完成。prod-2 磁盘使用率 92%,建议清理旧日志..." │
│ │
└──────────────────────────────────────────────────────────────────┘
```
### 5.8 通信配置页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Communication > Channels │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 通信渠道配置 ──────────────────────────────────────── │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 渠道 │ 状态 │ 配置 │ 操作 │ │
│ ├─────────────┼───────┼─────────────────────────┼─────────┤ │
│ │ App 推送 │ ✓ 启用│ FCM + WebSocket 自动启用 │ — │ │
│ │ 语音对话 │ ✓ 启用│ Pipecat (voice-service) │ [状态] │ │
│ │ 短信 (SMS) │ ✓ 启用│ Twilio +1-234-567-8900 │ [配置] │ │
│ │ 电话语音 │ ✓ 启用│ Pipecat 拨号 (via Twilio) │ [配置] │ │
│ │ 邮件 │ ✓ 启用│ smtp.gmail.com:587 │ [配置] │ │
│ │ Telegram │ ✓ 启用│ @IT0OpsBot │ [配置] │ │
│ │ 企业微信 │ ○ 禁用│ 未配置 │ [配置] │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ── 升级策略 ──────────────────────────────────────────── │
│ │
│ [默认策略 — 三层递进] │
│ Layer 1: App 推送 (FCM) → 立即 │
│ └─ 用户打开 App → 语音对话 (Pipecat voice-service) │
│ Layer 2: Pipecat 电话 (via Twilio) → 2 分钟无响应后 │
│ └─ Agent 接通即说话,直接语音对话(无 IVR 菜单) │
│ Layer 3: IM + 短信 → 5 分钟无响应后 │
│ Layer 4: 通知备用联系人 → 10 分钟无响应后 │
│ Layer 5: 邮件通知管理组 → 20 分钟无响应后 │
│ │
│ [编辑策略] [+ 新建策略] │
│ │
│ ── 联系人 ────────────────────────────────────────────── │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ 姓名 │ 手机 │ 邮箱 │ Telegram │ 角色 │ │
│ ├───────┼──────────────┼───────────────┼──────────┼──────┤ │
│ │ Admin │ 138-xxxx-xxxx│ admin@xx.com │ @admin │ 主要 │ │
│ │ Ops-1 │ 139-xxxx-xxxx│ ops1@xx.com │ — │ 备用 │ │
│ └────────────────────────────────────────────────────────┘ │
│ [+ 添加联系人] │
│ │
└──────────────────────────────────────────────────────────────────┘
```
### 5.9 多租户管理(超级管理员)
> 仅 `super_admin` 角色可见此模块。普通租户管理员只能在自己的租户范围内操作。
#### 5.8.1 顶栏租户选择器
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin [🔍 搜索...] [🔔 3] [租户: Acme Corp ▾] [👤 Admin] │
│ ┌─────────────────────────┐ │
│ │ ★ Acme Corp (当前) │ │
│ │ Globex Inc │ │
│ │ Initech Ltd │ │
│ │ ────────────────────── │ │
│ │ [管理所有租户] │ │
│ └─────────────────────────┘ │
└──────────────────────────────────────────────────────────────────┘
```
**行为**
- 切换租户后,所有页面数据刷新为目标租户的上下文
- API 请求自动注入 `X-Tenant-Id` 请求头
- Zustand `tenant-store` 持久化当前选中的租户 ID`localStorage`
- 页面 URL 不包含 tenantId通过 header 传递,避免 URL 泄露)
#### 5.8.2 租户列表页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Tenants │
├──────────────────────────────────────────────────────────────────┤
│ │
│ [+ 新建租户] [搜索租户...] 状态: [全部 ▾] 计划: [全部 ▾] │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ 租户ID │ 名称 │ 计划 │ 服务器 │ 用户 │ 状态 │ │
│ ├────────┼─────────────┼─────────────┼────────┼──────┼────────┤ │
│ │ t001 │ Acme Corp │ Enterprise │ 15/50 │ 8/20 │ ✓ 活跃 │ │
│ │ t002 │ Globex Inc │ Pro │ 5/20 │ 3/10 │ ✓ 活跃 │ │
│ │ t003 │ Initech Ltd │ Free │ 2/5 │ 1/3 │ ✓ 活跃 │ │
│ │ t004 │ Umbrella │ Pro │ 0/20 │ 1/10 │ ⏸ 暂停 │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ 共 4 个租户 · 活跃 3 · 暂停 1 │
│ │
└──────────────────────────────────────────────────────────────────┘
```
#### 5.8.3 租户详情/编辑页
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Tenants > Acme Corp │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ── 基本信息 ────────────────────────────────────────────────── │
│ │
│ 租户 ID: t001 (不可修改) │
│ 名称: [Acme Corp ] │
│ URL Slug: [acme-corp ] │
│ 状态: [● 活跃 ▾] (活跃 / 暂停 / 已删除) │
│ 所有者: admin@acme.com (user_id: xxx) │
│ │
│ ── 订阅计划与配额 ──────────────────────────────────────────── │
│ │
│ 当前计划: [Enterprise ▾] │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 资源 │ 配额 │ 已用 │ 使用率 │ 操作 │ │
│ ├───────────────┼────────┼────────┼─────────────────┼───────┤ │
│ │ 服务器数量 │ 50 │ 15 │ ████░░░░ 30% │ [调整]│ │
│ │ Skills 数量 │ 100 │ 23 │ ██░░░░░░ 23% │ [调整]│ │
│ │ 用户数量 │ 20 │ 8 │ ████░░░░ 40% │ [调整]│ │
│ │ 日会话上限 │ 500 │ 127 │ ██░░░░░░ 25% │ [调整]│ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
│ ── 允许的引擎 ────────────────────────────────────────────── │
│ ☑ Claude Code CLI ☑ Claude API ☐ Custom Agent │
│ │
│ ── 危险操作区 ────────────────────────────────────────────── │
│ [⏸ 暂停租户] [🗑 删除租户] (需二次确认 + 输入租户名称) │
│ │
│ ─────────────────────────────────────────────────────────── │
│ [取消] [保存修改] │
│ │
└──────────────────────────────────────────────────────────────────┘
```
#### 5.8.4 新建租户向导
```
┌──────────────────────────────────────────────────────────────────┐
│ IT0 Admin > Tenants > 新建租户 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ Step 1 of 3: 基本信息 │
│ ───────────────────────────────────────── │
│ 租户名称: [ ] │
│ URL Slug: [ ] (自动生成) │
│ 管理员邮箱: [ ] │
│ 管理员密码: [ ] │
│ │
│ Step 2 of 3: 选择计划 │
│ ───────────────────────────────────────── │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ Free │ │ Pro │ │ Enterprise │ │
│ │ 5 服务器 │ │ 20 服务器 │ │ 50 服务器 │ │
│ │ 3 用户 │ │ 10 用户 │ │ 20 用户 │ │
│ │ 10 Skills │ │ 50 Skills │ │ 100 Skills │ │
│ │ [选择] │ │ [★ 选择] │ │ [选择] │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
│ Step 3 of 3: 确认 │
│ ───────────────────────────────────────── │
│ 系统将自动: │
│ • 创建租户 Schema (it0_t_xxx) │
│ • 运行数据库迁移 │
│ • 创建管理员账户 │
│ • 初始化默认 System Prompt 和 Skills │
│ │
│ [上一步] [创建租户] │
│ │
└──────────────────────────────────────────────────────────────────┘
```
#### 5.8.5 Tenant StoreZustand
```typescript
// stores/zustand/tenant-store.ts
interface TenantState {
currentTenantId: string | null;
tenants: TenantSummary[]; // 当前用户可访问的租户列表
isLoading: boolean;
// Actions
switchTenant: (tenantId: string) => void;
fetchTenants: () => Promise<void>;
}
export const useTenantStore = create<TenantState>()(
persist(
(set, get) => ({
currentTenantId: null,
tenants: [],
isLoading: false,
switchTenant: (tenantId) => {
set({ currentTenantId: tenantId });
// 触发全局数据刷新 — invalidate 所有 TanStack Query
queryClient.invalidateQueries();
},
fetchTenants: async () => {
set({ isLoading: true });
const tenants = await apiClient.get<TenantSummary[]>('/tenants');
set({ tenants, isLoading: false });
// 如果尚未选择租户,自动选中第一个
if (!get().currentTenantId && tenants.length > 0) {
set({ currentTenantId: tenants[0].id });
}
},
}),
{ name: 'it0-tenant' } // localStorage key
)
);
```
#### 5.8.6 API Client 自动注入租户 Header
```typescript
// core/api/api-client.ts — 拦截器追加
const apiClient = {
async fetch(url: string, options: RequestInit = {}) {
const tenantId = useTenantStore.getState().currentTenantId;
const headers = new Headers(options.headers);
// ★ 自动注入租户标识
if (tenantId) {
headers.set('X-Tenant-Id', tenantId);
}
// JWT token 注入(已有逻辑)
const token = getAuthToken();
if (token) {
headers.set('Authorization', `Bearer ${token}`);
}
return fetch(`${API_BASE_URL}${url}`, { ...options, headers });
},
};
```
---
## 6. BFF 层Next.js API Routes
Web Admin 不直接调用后端微服务,而是通过 Next.js API Routes 做 BFF 代理:
```
Flutter App → API Gateway → 微服务
Web Admin → Next.js BFF → API Gateway → 微服务
```
```typescript
// app/api/proxy/[...path]/route.ts
import { NextRequest, NextResponse } from 'next/server';
/**
* 通用代理路由:
* /api/proxy/agent-service/config → http://api-gateway:8000/agent-service/config
* /api/proxy/ops-service/tasks → http://api-gateway:8000/ops-service/tasks
*
* 好处:
* 1. 前端不需要知道后端微服务的地址
* 2. 可以在 BFF 层做额外的数据聚合
* 3. SSR 页面可以在服务端直接调用
* 4. 统一处理认证 token 注入
*/
export async function GET(request: NextRequest, { params }: { params: { path: string[] } }) {
const backendUrl = `${process.env.API_GATEWAY_URL}/${params.path.join('/')}`;
const token = request.cookies.get('access_token')?.value;
const response = await fetch(backendUrl, {
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
});
const data = await response.json();
return NextResponse.json(data, { status: response.status });
}
export async function POST(request: NextRequest, { params }: { params: { path: string[] } }) {
const backendUrl = `${process.env.API_GATEWAY_URL}/${params.path.join('/')}`;
const token = request.cookies.get('access_token')?.value;
const body = await request.json();
const response = await fetch(backendUrl, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(body),
});
const data = await response.json();
return NextResponse.json(data, { status: response.status });
}
// PUT, DELETE 同理...
```
---
## 7. TanStack Query 数据请求层
```typescript
// core/api/query-keys.ts
export const queryKeys = {
servers: {
all: ['servers'] as const,
list: (filters?: ServerFilters) => [...queryKeys.servers.all, 'list', filters] as const,
detail: (id: string) => [...queryKeys.servers.all, 'detail', id] as const,
metrics: (id: string) => [...queryKeys.servers.all, 'metrics', id] as const,
},
runbooks: {
all: ['runbooks'] as const,
list: (filters?: RunbookFilters) => [...queryKeys.runbooks.all, 'list', filters] as const,
detail: (id: string) => [...queryKeys.runbooks.all, 'detail', id] as const,
},
alertRules: {
all: ['alertRules'] as const,
list: () => [...queryKeys.alertRules.all, 'list'] as const,
},
audit: {
all: ['audit'] as const,
list: (filters?: AuditFilters) => [...queryKeys.audit.all, 'list', filters] as const,
},
sessions: {
all: ['sessions'] as const,
list: (filters?: SessionFilters) => [...queryKeys.sessions.all, 'list', filters] as const,
detail: (id: string) => [...queryKeys.sessions.all, 'detail', id] as const,
},
} as const;
```
```typescript
// 使用示例 — Server 列表页
// hooks/use-servers.ts
export function useServers(filters?: ServerFilters) {
const apiClient = useApiClient();
return useQuery({
queryKey: queryKeys.servers.list(filters),
queryFn: () => apiClient.get<PaginatedResponse<Server>>('/api/proxy/inventory-service/servers', { params: filters }),
staleTime: 30 * 1000, // 30 秒内不重新请求
refetchInterval: 60 * 1000, // 每 60 秒自动刷新
});
}
export function useAddServer() {
const queryClient = useQueryClient();
const apiClient = useApiClient();
return useMutation({
mutationFn: (data: CreateServerDto) =>
apiClient.post('/api/proxy/inventory-service/servers', data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: queryKeys.servers.all });
},
});
}
```
---
## 8. WebSocket 实时通信
```typescript
// core/api/websocket-client.ts
class WebSocketClient {
private ws: WebSocket | null = null;
private listeners = new Map<string, Set<(data: any) => void>>();
private reconnectAttempts = 0;
private maxReconnectAttempts = 10;
constructor(private baseUrl: string) {}
connect(token: string) {
this.ws = new WebSocket(`${this.baseUrl}/ws?token=${token}`);
this.ws.onmessage = (event) => {
const data = JSON.parse(event.data);
const type = data.type as string;
this.listeners.get(type)?.forEach((cb) => cb(data));
this.listeners.get('*')?.forEach((cb) => cb(data)); // 通配符
};
this.ws.onclose = () => this.handleReconnect(token);
this.ws.onerror = () => this.handleReconnect(token);
}
on(eventType: string, callback: (data: any) => void) {
if (!this.listeners.has(eventType)) {
this.listeners.set(eventType, new Set());
}
this.listeners.get(eventType)!.add(callback);
return () => this.listeners.get(eventType)?.delete(callback);
}
private handleReconnect(token: string) {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
const delay = Math.pow(2, this.reconnectAttempts) * 1000;
setTimeout(() => {
this.reconnectAttempts++;
this.connect(token);
}, delay);
}
}
disconnect() {
this.ws?.close();
this.listeners.clear();
}
}
// React Hook 封装
export function useWebSocketEvent<T>(eventType: string, callback: (data: T) => void) {
const wsClient = useWebSocketClient(); // 从 Context 获取
useEffect(() => {
const unsubscribe = wsClient.on(eventType, callback);
return unsubscribe;
}, [wsClient, eventType, callback]);
}
```
---
## 9. 命令面板Cmd+K
类似 VS Code 的命令面板,提供全局快速操作:
```typescript
// components/ui/command-palette.tsx
const commands: Command[] = [
// 导航
{ id: 'nav-dashboard', label: '转到仪表盘', action: () => router.push('/dashboard'), category: '导航' },
{ id: 'nav-servers', label: '转到服务器管理', action: () => router.push('/servers'), category: '导航' },
{ id: 'nav-runbooks', label: '转到 Runbook', action: () => router.push('/runbooks'), category: '导航' },
{ id: 'nav-audit', label: '转到审计日志', action: () => router.push('/audit'), category: '导航' },
// 快捷操作
{ id: 'action-inspect', label: '发起全面巡检', action: () => createQuickTask('inspection'), category: '操作' },
{ id: 'action-terminal', label: '打开终端', action: () => router.push('/terminal'), category: '操作' },
{ id: 'action-switch-engine', label: '切换 Agent 引擎', action: () => openEngineSelector(), category: '配置' },
// 搜索
{ id: 'search-server', label: '搜索服务器...', action: () => openServerSearch(), category: '搜索' },
{ id: 'search-audit', label: '搜索审计日志...', action: () => openAuditSearch(), category: '搜索' },
];
// 使用 shadcn/ui 的 Command 组件
// Cmd+K (Mac) / Ctrl+K (Windows) 触发
```
---
## 10. 键盘快捷键
```typescript
// hooks/use-keyboard-shortcut.ts
export const globalShortcuts: KeyboardShortcut[] = [
{ key: 'k', meta: true, action: 'openCommandPalette', description: '命令面板' },
{ key: 'b', meta: true, action: 'toggleSidebar', description: '切换侧边栏' },
{ key: '1', meta: true, action: 'navDashboard', description: '仪表盘' },
{ key: '2', meta: true, action: 'navServers', description: '服务器' },
{ key: '3', meta: true, action: 'navRunbooks', description: 'Runbook' },
{ key: '4', meta: true, action: 'navAudit', description: '审计日志' },
{ key: '`', meta: true, action: 'openTerminal', description: '终端' },
{ key: 's', meta: true, action: 'save', description: '保存当前编辑' },
{ key: 'z', meta: true, action: 'undo', description: '撤销' },
{ key: 'z', meta: true, shift: true, action: 'redo', description: '重做' },
];
```
---
## 11. Provider 配置(根布局)
```typescript
// app/layout.tsx
import { ReduxProvider } from '@/stores/redux/provider';
import { QueryProvider } from '@/core/api/query-provider';
import { AuthProvider } from '@/core/auth/auth-provider';
import { WebSocketProvider } from '@/core/api/websocket-provider';
import { ThemeProvider } from '@/core/theme/theme-provider';
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="zh-CN" suppressHydrationWarning>
<body>
<ThemeProvider>
<AuthProvider>
<ReduxProvider>
<QueryProvider>
<WebSocketProvider>
{children}
</WebSocketProvider>
</QueryProvider>
</ReduxProvider>
</AuthProvider>
</ThemeProvider>
</body>
</html>
);
}
```
```typescript
// stores/redux/provider.tsx
'use client';
import { Provider } from 'react-redux';
import { createStore } from './store';
import { useRepositories } from '@/infrastructure/di';
export function ReduxProvider({ children }: { children: React.ReactNode }) {
const repositories = useRepositories();
const store = useMemo(() => createStore(repositories), [repositories]);
return <Provider store={store}>{children}</Provider>;
}
```
---
## 12. 侧边栏导航结构
```typescript
// components/layout/sidebar.tsx
const navigationItems: NavItem[] = [
{
label: '仪表盘',
icon: LayoutDashboard,
href: '/dashboard',
},
{
label: 'Agent 配置',
icon: Bot,
children: [
{ label: '引擎管理', href: '/agent-config', icon: Cpu },
{ label: 'Skills 管理', href: '/agent-config/skills', icon: Zap },
{ label: 'System Prompt', href: '/agent-config/prompts', icon: FileText },
{ label: 'Hook 脚本', href: '/agent-config/hooks', icon: Webhook },
],
},
{
label: 'Runbook',
icon: BookOpen,
href: '/runbooks',
},
{
label: '驻留指令',
icon: CalendarClock, // lucide-react
href: '/standing-orders',
},
{
label: '服务器',
icon: Server,
children: [
{ label: '服务器列表', href: '/servers', icon: List },
{ label: '集群管理', href: '/servers/clusters', icon: Network },
],
},
{
label: '安全',
icon: Shield,
children: [
{ label: '风险规则', href: '/security/risk-rules', icon: AlertTriangle },
{ label: '凭证管理', href: '/security/credentials', icon: Key },
{ label: '权限管理', href: '/security/permissions', icon: Users },
],
},
{
label: '监控',
icon: Activity,
children: [
{ label: '告警规则', href: '/monitoring/alert-rules', icon: Bell },
{ label: '健康检查', href: '/monitoring/health-checks', icon: HeartPulse },
{ label: '指标面板', href: '/monitoring/metrics', icon: BarChart },
],
},
{
label: '通信',
icon: MessageSquare,
children: [
{ label: '渠道配置', href: '/communication/channels', icon: Radio },
{ label: '联系人', href: '/communication/contacts', icon: UserCircle },
{ label: '升级策略', href: '/communication/escalation', icon: ArrowUpCircle },
],
},
{
label: '会话历史',
icon: History,
href: '/sessions',
},
{
label: '审计日志',
icon: ScrollText,
href: '/audit',
},
{
label: '终端',
icon: Terminal,
href: '/terminal',
},
// ★ 仅超级管理员可见
{
label: '租户管理',
icon: Building2,
href: '/tenants',
requiredRole: 'super_admin', // 路由守卫检查
},
];
```
---
## 13. 开发规范
### 13.1 文件命名
| 类型 | 规范 | 示例 |
|------|------|------|
| 组件文件 | kebab-case | `server-status-badge.tsx` |
| 页面文件 | `page.tsx` (Next.js 约定) | `app/(admin)/servers/page.tsx` |
| Store 文件 | kebab-case + 后缀 | `agent-config.slice.ts`, `ui-store.ts` |
| Hook 文件 | `use-` 前缀 | `use-servers.ts` |
| 实体 | kebab-case | `agent-engine.ts` |
| 工具函数 | kebab-case | `date.ts`, `format.ts` |
### 13.2 组件规范
```typescript
// 标准组件模板
interface Props {
// 明确 props 类型
}
export function ComponentName({ prop1, prop2 }: Props) {
// Hooks 在顶部
// 事件处理函数
// 渲染
return (/* JSX */);
}
```
- **Server Components 优先**:数据密集型列表页、详情页用 RSC
- **Client Components**:带交互的表单、实时更新、状态管理相关
- 文件顶部 `'use client'` 声明客户端组件
### 13.3 Zustand vs Redux 判断清单
| 问题 | 是 → Redux Toolkit | 否 → Zustand |
|------|-------------------|--------------|
| 状态变更需要审计追踪? | ✓ | |
| 需要撤销/重做? | ✓ | |
| 多个组件需要协调复杂的状态转换? | ✓ | |
| 状态涉及异步工作流(多步骤)? | ✓ | |
| 需要 Redux DevTools 调试? | ✓ | |
| 只是简单的 UI 开关/计数器? | | ✓ |
| 状态只在单个组件树内使用? | | ✓ |
---
## 14. 环境变量
```env
# .env.local
# API
NEXT_PUBLIC_APP_URL=http://localhost:3000
API_GATEWAY_URL=http://localhost:8000
# Auth
NEXTAUTH_SECRET=your-secret
NEXTAUTH_URL=http://localhost:3000
# WebSocket
NEXT_PUBLIC_WS_URL=ws://localhost:8000
```
---
## 15. 开发路线图
### Phase 1: 基础骨架Week 1-2
- [ ] Next.js 项目初始化App Router + Tailwind + shadcn/ui
- [ ] Clean Architecture 目录结构搭建
- [ ] Zustand + Redux Toolkit 配置
- [ ] TanStack Query 配置
- [ ] 认证流程(登录页 + JWT
- [ ] 布局框架(侧边栏 + 顶栏 + 面包屑)
- [ ] BFF 代理路由
### Phase 2: Agent 配置Week 3-4
- [ ] Agent 引擎管理页
- [ ] System Prompt 编辑器Monaco Editor
- [ ] Hook 脚本管理
- [ ] 配置保存 + 审计中间件
### Phase 3: 运维管理Week 5-6
- [ ] 服务器列表 + 详情 + 指标图表
- [ ] Runbook 列表 + 编辑器(含模板变量、测试)
- [ ] 仪表盘(摘要卡片 + 快捷操作)
### Phase 4: 安全与监控Week 7-8
- [ ] 风险规则管理(拖拽排序 + 规则测试)
- [ ] 凭证管理(加密展示 + 过期提醒)
- [ ] RBAC 权限矩阵
- [ ] 告警规则配置
- [ ] 健康检查配置
### Phase 5: 通信与审计Week 9-10
- [ ] 通信渠道配置
- [ ] 联系人管理
- [ ] 升级策略编辑器
- [ ] 审计日志(搜索 + 过滤 + 导出)
- [ ] 会话回放页面
### Phase 6: 高级功能Week 11-12
- [ ] 命令面板Cmd+K
- [ ] Web 终端xterm.js
- [ ] 全局键盘快捷键
- [ ] 暗色/亮色主题切换
- [ ] 响应式布局优化
---
## 16. package.json 参考
```json
{
"name": "it0-web-admin",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest",
"test:ui": "vitest --ui"
},
"dependencies": {
"next": "^14.2.0",
"react": "^18.3.0",
"react-dom": "^18.3.0",
"@reduxjs/toolkit": "^2.2.0",
"react-redux": "^9.1.0",
"zustand": "^4.5.0",
"@tanstack/react-query": "^5.50.0",
"@tanstack/react-table": "^8.19.0",
"react-hook-form": "^7.52.0",
"zod": "^3.23.0",
"@hookform/resolvers": "^3.9.0",
"tailwindcss": "^3.4.0",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"tailwind-merge": "^2.4.0",
"lucide-react": "^0.400.0",
"@monaco-editor/react": "^4.6.0",
"recharts": "^2.12.0",
"@xterm/xterm": "^5.5.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-web-links": "^0.11.0",
"next-auth": "^5.0.0",
"cmdk": "^1.0.0",
"sonner": "^1.5.0",
"@dnd-kit/core": "^6.1.0",
"@dnd-kit/sortable": "^8.0.0",
"date-fns": "^3.6.0"
},
"devDependencies": {
"typescript": "^5.5.0",
"@types/react": "^18.3.0",
"@types/react-dom": "^18.3.0",
"vitest": "^2.0.0",
"@testing-library/react": "^16.0.0",
"@testing-library/jest-dom": "^6.4.0",
"eslint": "^9.0.0",
"eslint-config-next": "^14.2.0",
"prettier": "^3.3.0",
"prettier-plugin-tailwindcss": "^0.6.0"
}
}
```