# 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 Admin(PC 端) | |------|---------------------|-------------------| | 角色 | 一线运维操作 | 管理配置 + 深度分析 | | 场景 | 随时随地审批、巡检、对话 | 坐在电脑前做系统配置 | | 核心功能 | 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 Routes(BFF 层) │ │ ├── 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((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()( 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; 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) => { state.activeEngine = action.payload; state.unsavedChanges = true; }, updateEngineConfig: (state, action: PayloadAction<{ type: EngineType; config: Partial }>) => { state.engines[action.payload.type] = { ...state.engines[action.payload.type], ...action.payload.config, }; state.unsavedChanges = true; }, setPromptDraft: (state, action: PayloadAction) => { state.currentPromptDraft = action.payload; state.unsavedChanges = true; }, addHookScript: (state, action: PayloadAction) => { state.hookScripts.push(action.payload); state.unsavedChanges = true; }, removeHookScript: (state, action: PayloadAction) => { 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; originalSnapshot: string | null; // JSON snapshot 用于变更对比 validationErrors: Record; testResult: RunbookTestResult | null; isTesting: boolean; history: RunbookHistoryEntry[]; // 撤销/重做栈 historyIndex: number; } const runbookEditorSlice = createSlice({ name: 'runbookEditor', initialState: { /* ... */ } as RunbookEditorState, reducers: { loadRunbook: (state, action: PayloadAction) => { 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>) => { 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['getState']>; export type AppDispatch = ReturnType['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 Store(Zustand) ```typescript // stores/zustand/tenant-store.ts interface TenantState { currentTenantId: string | null; tenants: TenantSummary[]; // 当前用户可访问的租户列表 isLoading: boolean; // Actions switchTenant: (tenantId: string) => void; fetchTenants: () => Promise; } export const useTenantStore = create()( 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('/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>('/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 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(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 ( {children} ); } ``` ```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 {children}; } ``` --- ## 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" } } ```