feat: 去 mock 化 —— 全模块接入真实数据 (参考 jianmu/opsx-jenkins/aiops)
后端(35+ 新路由): - AES-256-GCM 字段加密(enc:v1:),密钥自动生成 data/encryption.key - 新模型:Host/K8sCluster/Credential/BuildNode/DeployTool/WebhookEndpoint/ JenkinsServer/SystemSetting;Pipeline 扩展触发器/变量/缓存列, PipelineRun 扩展 buildNumber/externalUrl - 资源 CRUD + 真实测试端点:主机/集群/构建节点 TCP 拨号、 工具真实执行版本检测(保守安装策略)、Webhook HMAC-SHA256 签名发送、 入站流水线触发 /api/hooks/pipeline/:id - Jenkins 完整集成(移植 opsx-jenkins 生产客户端):crumb/BasicAuth/ buildWithParameters/Location 队列/progressiveText 增量控制台, 服务器 CRUD+测试、Job 树拉取(folder 压平)、导入为流水线、 jenkins 类型流水线真实执行回传日志 - 流水线列表附带 lastRun、运行重试、summary 统计、系统设置单行表 - 运行结束触发出站 Webhook 通知;GitOps 同步状态真实推进 前端(14 页全部去 mock): - 流水线列表/发布记录/批量发布/环境/凭证/构建节点/工具/Webhook/ 系统设置/Jenkins 导入/GitOps(资源树+差异) 全部接入真实 API - 编辑器 4 Tab 持久化(基本信息/触发设置含真实 Webhook URL/变量缓存/发布历史) - TopBar 真实流水线切换 + 仅保存/保存并运行 - Sidebar 徽标真实统计;删除未使用组件与 mock 数据文件
This commit is contained in:
1 parent
04d1342b27
commit
26341c3e8a
37 files changed
+5702
-4683
No files matched your search
+47
-30
@@ -1,5 +1,6 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Toaster } from 'sonner';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import Sidebar from '@/components/Sidebar';
|
||||
import TopBar from '@/components/TopBar';
|
||||
import PipelineFlowEditor from '@/components/flow/PipelineFlowEditor';
|
||||
@@ -19,7 +20,7 @@ import WebhookConfig from '@/pages/WebhookConfig';
|
||||
import GitOpsDeploy from '@/pages/GitOpsDeploy';
|
||||
import PluginManager from '@/pages/PluginManager';
|
||||
import SystemSettings from '@/pages/SystemSettings';
|
||||
import { pipelineScenarios } from '@/data/pipelineData';
|
||||
import { apiClient } from '@/lib/api';
|
||||
|
||||
export type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'settings';
|
||||
export type EditorTab = '基本信息' | '流程配置' | '触发设置' | '变量和缓存' | '发布';
|
||||
@@ -31,19 +32,27 @@ export default function App() {
|
||||
|
||||
// Pipeline list/editor view toggle
|
||||
const [pipelineView, setPipelineView] = useState<'list' | 'editor'>('list');
|
||||
// Current editing pipeline ID for flow editor
|
||||
const [currentPipelineId, setCurrentPipelineId] = useState<number>(1);
|
||||
// Pipeline editor state (only used when in pipeline editor mode)
|
||||
const [activeScenario, setActiveScenario] = useState<'go-backend' | 'vue-frontend'>('go-backend');
|
||||
const [activeTab, setActiveTab] = useState<EditorTab>('流程配置');
|
||||
const [isSimulating, setIsSimulating] = useState(false);
|
||||
const [executionStatus, setExecutionStatus] = useState<'idle' | 'running' | 'success' | 'failed'>('idle');
|
||||
const [saveTick, setSaveTick] = useState(0);
|
||||
const [runTick, setRunTick] = useState(0);
|
||||
|
||||
const scenario = pipelineScenarios[activeScenario];
|
||||
const { data: pipelines = [] } = useQuery({
|
||||
queryKey: ['pipelines'],
|
||||
queryFn: apiClient.listPipelines,
|
||||
});
|
||||
|
||||
const handleScenarioChange = (key: 'go-backend' | 'vue-frontend') => {
|
||||
setActiveScenario(key);
|
||||
};
|
||||
// 当前流水线最近一次运行状态(TopBar 徽章)
|
||||
const { data: currentRuns = [] } = useQuery({
|
||||
queryKey: ['runs', { pipelineId: currentPipelineId }],
|
||||
queryFn: () => apiClient.listRuns({ pipelineId: currentPipelineId }),
|
||||
refetchInterval: (query) =>
|
||||
query.state.data?.[0]?.status === 'running' || query.state.data?.[0]?.status === 'pending' ? 2000 : false,
|
||||
});
|
||||
|
||||
const currentPipeline = pipelines.find((p: any) => p.id === currentPipelineId);
|
||||
const latestStatus = currentRuns[0]?.status as string | undefined;
|
||||
const isPipelineRunning = latestStatus === 'running' || latestStatus === 'pending';
|
||||
|
||||
const handleSelectPipeline = useCallback((id: string) => {
|
||||
setCurrentPipelineId(Number(id) || 1);
|
||||
@@ -51,13 +60,19 @@ export default function App() {
|
||||
setActiveTab('流程配置');
|
||||
}, []);
|
||||
|
||||
const handleSimulateRun = useCallback(() => {
|
||||
setIsSimulating(true);
|
||||
setExecutionStatus('running');
|
||||
setTimeout(() => {
|
||||
setIsSimulating(false);
|
||||
setExecutionStatus('success');
|
||||
}, 3000);
|
||||
const handlePipelineSwitch = useCallback((id: number) => {
|
||||
setCurrentPipelineId(id);
|
||||
setActiveTab('流程配置');
|
||||
}, []);
|
||||
|
||||
const handleOnlySave = useCallback(() => {
|
||||
if (activeTab !== '流程配置') setActiveTab('流程配置');
|
||||
setSaveTick((t) => t + 1);
|
||||
}, [activeTab]);
|
||||
|
||||
const handleSaveAndRun = useCallback(() => {
|
||||
setActiveTab('流程配置');
|
||||
setRunTick((t) => t + 1);
|
||||
}, []);
|
||||
|
||||
// Render content based on active module
|
||||
@@ -71,40 +86,42 @@ export default function App() {
|
||||
return (
|
||||
<div className="h-full flex flex-col">
|
||||
<TopBar
|
||||
pipelineName={scenario.name}
|
||||
pipelineName={currentPipeline?.name || '...'}
|
||||
pipelines={pipelines.map((p: any) => ({ id: p.id, name: p.name }))}
|
||||
currentPipelineId={currentPipelineId}
|
||||
onPipelineChange={handlePipelineSwitch}
|
||||
activeTab={activeTab}
|
||||
onTabChange={setActiveTab}
|
||||
onSimulateRun={handleSimulateRun}
|
||||
isSimulating={isSimulating}
|
||||
scenarioKey={activeScenario}
|
||||
onScenarioChange={handleScenarioChange}
|
||||
executionStatus={executionStatus}
|
||||
onOnlySave={handleOnlySave}
|
||||
onSaveAndRun={handleSaveAndRun}
|
||||
isRunning={isPipelineRunning}
|
||||
executionStatus={(latestStatus as 'running' | 'success' | 'failed' | 'cancelled') || 'idle'}
|
||||
onBack={() => setPipelineView('list')}
|
||||
/>
|
||||
<div className="flex-1 flex overflow-hidden">
|
||||
{activeTab === '流程配置' && (
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<PipelineFlowEditor pipelineId={currentPipelineId} />
|
||||
<PipelineFlowEditor pipelineId={currentPipelineId} saveSignal={saveTick} runSignal={runTick} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === '基本信息' && (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<BasicInfoPanel />
|
||||
<BasicInfoPanel pipelineId={currentPipelineId} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === '触发设置' && (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<TriggerPanel />
|
||||
<TriggerPanel pipelineId={currentPipelineId} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === '变量和缓存' && (
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<VariablesPanel />
|
||||
<VariablesPanel pipelineId={currentPipelineId} />
|
||||
</div>
|
||||
)}
|
||||
{activeTab === '发布' && (
|
||||
<div className="flex-1 overflow-hidden">
|
||||
<ReleasePanel />
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
<ReleasePanel pipelineId={currentPipelineId} />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -121,7 +138,7 @@ export default function App() {
|
||||
case 'jenkins-import':
|
||||
return <JenkinsImport />;
|
||||
case 'batch-release':
|
||||
return <BatchRelease />;
|
||||
return <BatchRelease onViewRecords={() => setActiveModule('release-records')} />;
|
||||
case 'release-records':
|
||||
return <ReleaseRecords />;
|
||||
case 'webhooks':
|
||||
|
||||
Reference in new issue
Block a user