refactor: Webhook 移入系统设置「消息通知」Tab,取消独立菜单
- WebhookConfig 页面转为可嵌入卡片组件 WebhookManager (标准设置卡样式,逻辑不变),渲染于飞书通知卡下方 - 移除侧边栏「Webhook」菜单项、/webhooks 路由与页面文件 (旧地址自动回退流水线页) - 使用指南/接口文档同步更新 Webhook 配置入口描述
This commit is contained in:
1 parent
ed88a18e03
commit
695c1c2357
6 files changed
+22
-23
No files matched your search
+1
-1
@@ -85,7 +85,7 @@ curl -X POST http://localhost:9527/api/ai/release \
|
|||||||
| `/api/build-nodes/:id/disk-test` | POST | dd 磁盘性能实测(256MB,本机 exec 或 SSH 远程;返回读写 MB/s) |
|
| `/api/build-nodes/:id/disk-test` | POST | dd 磁盘性能实测(256MB,本机 exec 或 SSH 远程;返回读写 MB/s) |
|
||||||
| `/api/tools` | GET/PUT | 工具清单与安装策略 |
|
| `/api/tools` | GET/PUT | 工具清单与安装策略 |
|
||||||
| `/api/tools/:id/check` | POST | 真实执行版本探测命令 |
|
| `/api/tools/:id/check` | POST | 真实执行版本探测命令 |
|
||||||
| `/api/webhooks` | CRUD + `/:id/test` | 出站 Webhook(HMAC-SHA256 签名发送) |
|
| `/api/webhooks` | CRUD + `/:id/test` | 出站 Webhook(HMAC-SHA256 签名发送,配置入口:系统设置→消息通知) |
|
||||||
| `/api/hooks/pipeline/:id?token=` | POST | 入站触发流水线(免鉴权,token 校验) |
|
| `/api/hooks/pipeline/:id?token=` | POST | 入站触发流水线(免鉴权,token 校验) |
|
||||||
|
|
||||||
## Jenkins
|
## Jenkins
|
||||||
|
|||||||
@@ -89,13 +89,15 @@ cex-agent-admin
|
|||||||
- 「检测」真实执行版本探测命令(如 `java -version`);「安装」是保守策略——只检测不自动下载
|
- 「检测」真实执行版本探测命令(如 `java -version`);「安装」是保守策略——只检测不自动下载
|
||||||
- `deploy/Dockerfile.buildnode` 一键构建 Ubuntu 构建节点镜像
|
- `deploy/Dockerfile.buildnode` 一键构建 Ubuntu 构建节点镜像
|
||||||
|
|
||||||
## 飞书 / Lark 通知
|
## 飞书 / Lark 通知与 Webhook
|
||||||
|
|
||||||
**是什么**:发布成功/失败自动向群推送结果卡片(应用/分支/构建/状态/时间 + 「查看构建详情」按钮直达 Jenkins)。
|
**是什么**:发布成功/失败自动向群推送结果卡片(应用/分支/构建/状态/时间 + 「查看构建详情」按钮直达 Jenkins)。
|
||||||
|
|
||||||
**怎么用**:系统设置 → 消息通知 → 填群机器人 Webhook(和可选的签名密钥)→「发送测试卡片」验证。
|
**怎么用**:系统设置 → 消息通知 → 填群机器人 Webhook(和可选的签名密钥)→「发送测试卡片」验证。
|
||||||
|
|
||||||
**技巧**:单条流水线可在「基本信息」里单独填通知 Webhook,优先于全局配置。
|
**技巧**:
|
||||||
|
- 单条流水线可在「基本信息」里单独填通知 Webhook,优先于全局配置
|
||||||
|
- 通用 Webhook 回调(发给第三方系统,HMAC-SHA256 签名)也在系统设置 → 消息通知里配置
|
||||||
|
|
||||||
## 用户与 LDAP
|
## 用户与 LDAP
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import ToolConfig from '@/pages/ToolConfig';
|
|||||||
import JenkinsImport from '@/pages/JenkinsImport';
|
import JenkinsImport from '@/pages/JenkinsImport';
|
||||||
import BatchRelease from '@/pages/BatchRelease';
|
import BatchRelease from '@/pages/BatchRelease';
|
||||||
import ReleaseRecords from '@/pages/ReleaseRecords';
|
import ReleaseRecords from '@/pages/ReleaseRecords';
|
||||||
import WebhookConfig from '@/pages/WebhookConfig';
|
|
||||||
import GitOpsDeploy from '@/pages/GitOpsDeploy';
|
import GitOpsDeploy from '@/pages/GitOpsDeploy';
|
||||||
import PluginManager from '@/pages/PluginManager';
|
import PluginManager from '@/pages/PluginManager';
|
||||||
import SystemSettings from '@/pages/SystemSettings';
|
import SystemSettings from '@/pages/SystemSettings';
|
||||||
@@ -27,7 +26,7 @@ import UserManage from '@/pages/UserManage';
|
|||||||
import Docs from '@/pages/Docs';
|
import Docs from '@/pages/Docs';
|
||||||
import { apiClient, auth } from '@/lib/api';
|
import { apiClient, auth } from '@/lib/api';
|
||||||
|
|
||||||
export type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'users' | 'docs' | 'settings';
|
export type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'plugins' | 'users' | 'docs' | 'settings';
|
||||||
export type EditorTab = '基本信息' | '流程配置' | '触发设置' | '变量和缓存' | '发布';
|
export type EditorTab = '基本信息' | '流程配置' | '触发设置' | '变量和缓存' | '发布';
|
||||||
|
|
||||||
// 路由 ↔ 模块映射(用户可直接访问 /release、/pipelines/1 等)
|
// 路由 ↔ 模块映射(用户可直接访问 /release、/pipelines/1 等)
|
||||||
@@ -42,7 +41,6 @@ const ROUTE_TO_MODULE: Record<string, ModuleType> = {
|
|||||||
'/build-nodes': 'build-nodes',
|
'/build-nodes': 'build-nodes',
|
||||||
'/tools': 'tools',
|
'/tools': 'tools',
|
||||||
'/jenkins-import': 'jenkins-import',
|
'/jenkins-import': 'jenkins-import',
|
||||||
'/webhooks': 'webhooks',
|
|
||||||
'/plugins': 'plugins',
|
'/plugins': 'plugins',
|
||||||
'/users': 'users',
|
'/users': 'users',
|
||||||
'/docs': 'docs',
|
'/docs': 'docs',
|
||||||
@@ -59,7 +57,6 @@ const MODULE_TO_ROUTE: Record<ModuleType, string> = {
|
|||||||
'build-nodes': '/build-nodes',
|
'build-nodes': '/build-nodes',
|
||||||
tools: '/tools',
|
tools: '/tools',
|
||||||
'jenkins-import': '/jenkins-import',
|
'jenkins-import': '/jenkins-import',
|
||||||
webhooks: '/webhooks',
|
|
||||||
plugins: '/plugins',
|
plugins: '/plugins',
|
||||||
users: '/users',
|
users: '/users',
|
||||||
docs: '/docs',
|
docs: '/docs',
|
||||||
@@ -230,8 +227,6 @@ export default function App() {
|
|||||||
return <BatchRelease onViewRecords={() => goToModule('release-records')} />;
|
return <BatchRelease onViewRecords={() => goToModule('release-records')} />;
|
||||||
case 'release-records':
|
case 'release-records':
|
||||||
return <ReleaseRecords />;
|
return <ReleaseRecords />;
|
||||||
case 'webhooks':
|
|
||||||
return <WebhookConfig />;
|
|
||||||
case 'gitops':
|
case 'gitops':
|
||||||
return <GitOpsDeploy />;
|
return <GitOpsDeploy />;
|
||||||
case 'plugins':
|
case 'plugins':
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import {
|
|||||||
Rocket,
|
Rocket,
|
||||||
Zap,
|
Zap,
|
||||||
History,
|
History,
|
||||||
Webhook,
|
|
||||||
CloudSync,
|
CloudSync,
|
||||||
Puzzle,
|
Puzzle,
|
||||||
Settings,
|
Settings,
|
||||||
@@ -24,7 +23,7 @@ import { useQuery } from '@tanstack/react-query';
|
|||||||
import { apiClient } from '@/lib/api';
|
import { apiClient } from '@/lib/api';
|
||||||
import { useTheme } from '@/providers/theme';
|
import { useTheme } from '@/providers/theme';
|
||||||
|
|
||||||
type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'users' | 'docs' | 'settings';
|
type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'plugins' | 'users' | 'docs' | 'settings';
|
||||||
|
|
||||||
interface SidebarProps {
|
interface SidebarProps {
|
||||||
activeModule: ModuleType;
|
activeModule: ModuleType;
|
||||||
@@ -56,7 +55,6 @@ const menuGroups: { label: string; items: { id: ModuleType; label: string; icon:
|
|||||||
label: '集成',
|
label: '集成',
|
||||||
items: [
|
items: [
|
||||||
{ id: 'jenkins-import', label: 'Jenkins导入', icon: Download, badge: null },
|
{ id: 'jenkins-import', label: 'Jenkins导入', icon: Download, badge: null },
|
||||||
{ id: 'webhooks', label: 'Webhook', icon: Webhook, badge: null },
|
|
||||||
{ id: 'tools', label: '工具配置', icon: Wrench, badge: null },
|
{ id: 'tools', label: '工具配置', icon: Wrench, badge: null },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
+10
-11
@@ -49,7 +49,7 @@ const formatTime = (v: string | null | undefined) => {
|
|||||||
|
|
||||||
const emptyForm = { name: '', url: '', secret: '', events: [] as string[] };
|
const emptyForm = { name: '', url: '', secret: '', events: [] as string[] };
|
||||||
|
|
||||||
export default function WebhookConfig() {
|
export default function WebhookManager() {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
const [showForm, setShowForm] = useState(false);
|
const [showForm, setShowForm] = useState(false);
|
||||||
const [editingId, setEditingId] = useState<number | null>(null);
|
const [editingId, setEditingId] = useState<number | null>(null);
|
||||||
@@ -137,14 +137,13 @@ export default function WebhookConfig() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="h-full flex flex-col" style={{ background: 'var(--canvas-bg)' }}>
|
<div className="rounded-lg border overflow-hidden" style={{ background: 'var(--panel-bg)', borderColor: 'var(--border-color)' }}>
|
||||||
{/* Header */}
|
{/* Card Header */}
|
||||||
<div className="px-5 py-3 border-b flex items-center justify-between flex-shrink-0" style={{ background: 'var(--panel-bg)', borderColor: 'var(--border-color)' }}>
|
<div className="px-5 py-3 border-b flex items-center justify-between" style={{ borderColor: 'var(--border-color)', background: 'var(--panel-bg-secondary)' }}>
|
||||||
<div className="flex items-center gap-3">
|
<div className="flex items-center gap-2">
|
||||||
<h1 className="text-[15px] font-semibold" style={{ color: 'var(--text-primary)' }}>Webhook 配置</h1>
|
<Webhook className="w-4 h-4" style={{ color: 'var(--accent-blue)' }} />
|
||||||
<span className="text-[11px] px-2 py-[2px] rounded-full font-medium" style={{ background: 'var(--panel-bg-secondary)', color: 'var(--text-tertiary)' }}>
|
<span className="text-[13px] font-semibold" style={{ color: 'var(--text-primary)' }}>Webhook 通知</span>
|
||||||
{webhooks.length}
|
<span className="text-[10px]" style={{ color: 'var(--text-tertiary)' }}>发布事件回调第三方(HMAC-SHA256 签名)</span>
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
onClick={() => { setShowForm(true); setEditingId(null); setEditingSecretSet(false); setForm(emptyForm); }}
|
onClick={() => { setShowForm(true); setEditingId(null); setEditingSecretSet(false); setForm(emptyForm); }}
|
||||||
@@ -155,8 +154,8 @@ export default function WebhookConfig() {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Content */}
|
{/* Body */}
|
||||||
<div className="flex-1 overflow-y-auto custom-scrollbar p-5">
|
<div className="p-5">
|
||||||
{showForm && (
|
{showForm && (
|
||||||
<div className="rounded-lg border p-4 mb-4" style={{ background: 'var(--panel-bg)', borderColor: 'var(--border-color)' }}>
|
<div className="rounded-lg border p-4 mb-4" style={{ background: 'var(--panel-bg)', borderColor: 'var(--border-color)' }}>
|
||||||
<h3 className="text-[14px] font-semibold mb-3" style={{ color: 'var(--text-primary)' }}>{editingId != null ? '编辑 Webhook' : '新建 Webhook'}</h3>
|
<h3 className="text-[14px] font-semibold mb-3" style={{ color: 'var(--text-primary)' }}>{editingId != null ? '编辑 Webhook' : '新建 Webhook'}</h3>
|
||||||
@@ -7,6 +7,7 @@ import { toast } from 'sonner';
|
|||||||
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
||||||
import { apiClient } from '@/lib/api';
|
import { apiClient } from '@/lib/api';
|
||||||
import { useTheme, type ThemeMode } from '@/providers/theme';
|
import { useTheme, type ThemeMode } from '@/providers/theme';
|
||||||
|
import WebhookManager from '@/components/settings/WebhookManager';
|
||||||
|
|
||||||
interface ThemeConfig {
|
interface ThemeConfig {
|
||||||
mode: 'light' | 'dark' | 'system';
|
mode: 'light' | 'dark' | 'system';
|
||||||
@@ -638,6 +639,10 @@ export default function SystemSettings() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4">
|
||||||
|
<WebhookManager />
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user