it0/docs/web-admin-guide.md

111 KiB
Raw Blame History

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 示例

// 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 }),
}));
// 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 示例

// 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;
// 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 配置

// 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 审计中间件(配置变更自动追踪)

// 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 持久化当前选中的租户 IDlocalStorage
  • 页面 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

// 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

// 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  →  微服务
// 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 数据请求层

// 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;
// 使用示例 — 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 实时通信

// 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 的命令面板,提供全局快速操作:

// 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. 键盘快捷键

// 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 配置(根布局)

// 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>
  );
}
// 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. 侧边栏导航结构

// 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 组件规范

// 标准组件模板
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.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 参考

{
  "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"
  }
}