feat: FlowStack 发布系统首个可运行版本 (gin + React)

- Go gin 后端:流水线/节点/连线/运行/日志/GitOps/插件 REST API,
  SQLite(GORM) 自动建表 + seed,模拟执行引擎 + SSE 实时日志推送,
  前端产物 go:embed 嵌入单二进制,一个端口同时服务 API 与页面
- React 19 前端:画布式流水线编辑器(React Flow + Tekton 任务库)、
  GitOps 部署、批量发布、发布记录等 14 个模块页面,
  tRPC 全部替换为 REST(TanStack Query)
- 首次启动生成随机密码管理员账号(日志 + data/initial-admin-password.txt)
- run.sh 一键构建启动(默认端口 9527,支持 start/stop/restart/build/dev)
This commit is contained in:
Logan.Li committed 2026-10-27 02:44:10 +08:00
commit 04d1342b27
115 files changed
+23268

No files matched your search

+154
View File
@@ -0,0 +1,154 @@
import { useState, useCallback } from 'react';
import { Toaster } from 'sonner';
import Sidebar from '@/components/Sidebar';
import TopBar from '@/components/TopBar';
import PipelineFlowEditor from '@/components/flow/PipelineFlowEditor';
import BasicInfoPanel from '@/components/BasicInfoPanel';
import TriggerPanel from '@/components/TriggerPanel';
import VariablesPanel from '@/components/VariablesPanel';
import ReleasePanel from '@/components/ReleasePanel';
import PipelineList from '@/pages/PipelineList';
import EnvironmentManager from '@/pages/EnvironmentManager';
import CredentialManager from '@/pages/CredentialManager';
import BuildNodeManager from '@/pages/BuildNodeManager';
import ToolConfig from '@/pages/ToolConfig';
import JenkinsImport from '@/pages/JenkinsImport';
import BatchRelease from '@/pages/BatchRelease';
import ReleaseRecords from '@/pages/ReleaseRecords';
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';
export type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'settings';
export type EditorTab = '基本信息' | '流程配置' | '触发设置' | '变量和缓存' | '发布';
export default function App() {
// Module navigation
const [activeModule, setActiveModule] = useState<ModuleType>('pipelines');
const [sidebarCollapsed, setSidebarCollapsed] = useState(false);
// 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 scenario = pipelineScenarios[activeScenario];
const handleScenarioChange = (key: 'go-backend' | 'vue-frontend') => {
setActiveScenario(key);
};
const handleSelectPipeline = useCallback((id: string) => {
setCurrentPipelineId(Number(id) || 1);
setPipelineView('editor');
setActiveTab('流程配置');
}, []);
const handleSimulateRun = useCallback(() => {
setIsSimulating(true);
setExecutionStatus('running');
setTimeout(() => {
setIsSimulating(false);
setExecutionStatus('success');
}, 3000);
}, []);
// Render content based on active module
const renderContent = () => {
switch (activeModule) {
case 'pipelines':
if (pipelineView === 'list') {
return <PipelineList onSelectPipeline={handleSelectPipeline} />;
}
// Pipeline editor mode
return (
<div className="h-full flex flex-col">
<TopBar
pipelineName={scenario.name}
activeTab={activeTab}
onTabChange={setActiveTab}
onSimulateRun={handleSimulateRun}
isSimulating={isSimulating}
scenarioKey={activeScenario}
onScenarioChange={handleScenarioChange}
executionStatus={executionStatus}
onBack={() => setPipelineView('list')}
/>
<div className="flex-1 flex overflow-hidden">
{activeTab === '流程配置' && (
<div className="flex-1 overflow-hidden">
<PipelineFlowEditor pipelineId={currentPipelineId} />
</div>
)}
{activeTab === '基本信息' && (
<div className="flex-1 overflow-y-auto">
<BasicInfoPanel />
</div>
)}
{activeTab === '触发设置' && (
<div className="flex-1 overflow-y-auto">
<TriggerPanel />
</div>
)}
{activeTab === '变量和缓存' && (
<div className="flex-1 overflow-y-auto">
<VariablesPanel />
</div>
)}
{activeTab === '发布' && (
<div className="flex-1 overflow-hidden">
<ReleasePanel />
</div>
)}
</div>
</div>
);
case 'environments':
return <EnvironmentManager />;
case 'credentials':
return <CredentialManager />;
case 'build-nodes':
return <BuildNodeManager />;
case 'tools':
return <ToolConfig />;
case 'jenkins-import':
return <JenkinsImport />;
case 'batch-release':
return <BatchRelease />;
case 'release-records':
return <ReleaseRecords />;
case 'webhooks':
return <WebhookConfig />;
case 'gitops':
return <GitOpsDeploy />;
case 'plugins':
return <PluginManager />;
case 'settings':
return <SystemSettings />;
default:
return <PipelineList onSelectPipeline={handleSelectPipeline} />;
}
};
return (
<div className="h-screen w-screen flex overflow-hidden" style={{ background: 'var(--canvas-bg)', color: 'var(--text-primary)' }}>
<Toaster position="top-right" richColors />
<Sidebar
activeModule={activeModule}
onModuleChange={setActiveModule}
collapsed={sidebarCollapsed}
onToggleCollapse={() => setSidebarCollapsed(!sidebarCollapsed)}
/>
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
{renderContent()}
</div>
</div>
);
}