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:
commit
04d1342b27
115 files changed
+23268
No files matched your search
@@ -0,0 +1,167 @@
|
||||
import {
|
||||
GitBranch,
|
||||
Server,
|
||||
Key,
|
||||
Cpu,
|
||||
Wrench,
|
||||
Download,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Rocket,
|
||||
Zap,
|
||||
History,
|
||||
Webhook,
|
||||
CloudSync,
|
||||
Puzzle,
|
||||
Settings
|
||||
} from 'lucide-react';
|
||||
|
||||
type ModuleType = 'pipelines' | 'gitops' | 'batch-release' | 'release-records' | 'environments' | 'credentials' | 'build-nodes' | 'tools' | 'jenkins-import' | 'webhooks' | 'plugins' | 'settings';
|
||||
|
||||
interface SidebarProps {
|
||||
activeModule: ModuleType;
|
||||
onModuleChange: (module: ModuleType) => void;
|
||||
collapsed: boolean;
|
||||
onToggleCollapse: () => void;
|
||||
}
|
||||
|
||||
const menuGroups: { label: string; items: { id: ModuleType; label: string; icon: typeof GitBranch; badge: string | null }[] }[] = [
|
||||
{
|
||||
label: '发布',
|
||||
items: [
|
||||
{ id: 'pipelines', label: '流水线', icon: GitBranch, badge: null },
|
||||
{ id: 'gitops', label: 'GitOps 部署', icon: CloudSync, badge: 'New' },
|
||||
{ id: 'batch-release', label: '批量发布', icon: Zap, badge: null },
|
||||
{ id: 'release-records', label: '发布记录', icon: History, badge: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '资源',
|
||||
items: [
|
||||
{ id: 'environments', label: '环境管理', icon: Server, badge: null },
|
||||
{ id: 'credentials', label: '凭证管理', icon: Key, badge: null },
|
||||
{ id: 'build-nodes', label: '构建节点', icon: Cpu, badge: '3' },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '集成',
|
||||
items: [
|
||||
{ id: 'jenkins-import', label: 'Jenkins导入', icon: Download, badge: null },
|
||||
{ id: 'webhooks', label: 'Webhook', icon: Webhook, badge: null },
|
||||
{ id: 'tools', label: '工具配置', icon: Wrench, badge: null },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: '系统',
|
||||
items: [
|
||||
{ id: 'plugins', label: '插件市场', icon: Puzzle, badge: '12' },
|
||||
{ id: 'settings', label: '系统设置', icon: Settings, badge: null },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function Sidebar({ activeModule, onModuleChange, collapsed, onToggleCollapse }: SidebarProps) {
|
||||
return (
|
||||
<div
|
||||
className="flex flex-col border-r transition-all duration-300 ease-in-out h-full"
|
||||
style={{
|
||||
width: collapsed ? '56px' : '200px',
|
||||
background: 'var(--panel-bg)',
|
||||
borderColor: 'var(--border-color)',
|
||||
}}
|
||||
>
|
||||
{/* Logo area */}
|
||||
<div
|
||||
className="flex items-center gap-2 px-3 h-12 border-b flex-shrink-0"
|
||||
style={{ borderColor: 'var(--border-color)' }}
|
||||
>
|
||||
<div
|
||||
className="w-7 h-7 rounded-md flex items-center justify-center flex-shrink-0"
|
||||
style={{ background: 'var(--accent-blue)' }}
|
||||
>
|
||||
<Rocket className="w-4 h-4 text-white" />
|
||||
</div>
|
||||
{!collapsed && (
|
||||
<span className="text-[13px] font-semibold truncate" style={{ color: 'var(--text-primary)' }}>
|
||||
应用发布
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Menu groups */}
|
||||
<div className="flex-1 py-2 overflow-y-auto custom-scrollbar">
|
||||
{menuGroups.map((group) => (
|
||||
<div key={group.label} className="mb-2">
|
||||
{!collapsed && (
|
||||
<div
|
||||
className="px-3 py-1 text-[10px] font-semibold uppercase tracking-wider"
|
||||
style={{ color: 'var(--text-tertiary)' }}
|
||||
>
|
||||
{group.label}
|
||||
</div>
|
||||
)}
|
||||
<div className="px-2 space-y-[2px]">
|
||||
{group.items.map((item) => {
|
||||
const Icon = item.icon;
|
||||
const isActive = activeModule === item.id;
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => onModuleChange(item.id)}
|
||||
className="w-full flex items-center gap-2.5 px-2.5 py-[7px] rounded-md transition-all duration-150 group relative"
|
||||
style={{
|
||||
background: isActive ? 'var(--accent-blue-light)' : 'transparent',
|
||||
color: isActive ? 'var(--accent-blue)' : 'var(--text-secondary)',
|
||||
}}
|
||||
title={collapsed ? item.label : undefined}
|
||||
>
|
||||
<Icon className="w-[18px] h-[18px] flex-shrink-0" />
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span className="text-[13px] font-medium truncate flex-1 text-left">
|
||||
{item.label}
|
||||
</span>
|
||||
{item.badge && (
|
||||
<span
|
||||
className="text-[9px] px-1 py-[1px] rounded font-medium"
|
||||
style={{
|
||||
background: item.badge === 'New' ? '#ddf4ff' : 'var(--panel-bg-secondary)',
|
||||
color: item.badge === 'New' ? 'var(--accent-blue)' : 'var(--text-tertiary)',
|
||||
}}
|
||||
>
|
||||
{item.badge}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{isActive && !collapsed && (
|
||||
<div
|
||||
className="absolute left-0 top-1/2 -translate-y-1/2 w-[3px] h-5 rounded-r-full"
|
||||
style={{ background: 'var(--accent-blue)' }}
|
||||
/>
|
||||
)}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Collapse toggle */}
|
||||
<div
|
||||
className="px-2 py-2 border-t flex justify-center"
|
||||
style={{ borderColor: 'var(--border-color)' }}
|
||||
>
|
||||
<button
|
||||
onClick={onToggleCollapse}
|
||||
className="p-1.5 rounded-md transition-colors hover:bg-black/5"
|
||||
style={{ color: 'var(--text-tertiary)' }}
|
||||
title={collapsed ? '展开' : '收起'}
|
||||
>
|
||||
{collapsed ? <ChevronRight className="w-4 h-4" /> : <ChevronLeft className="w-4 h-4" />}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user