Files
FlowStack/backend/internal/handler/gitops.go
T
Logan.Li 04d1342b27 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)
2026-10-27 02:44:10 +08:00

68 lines
2.7 KiB
Go

package handler
import (
"net/http"
"github.com/gin-gonic/gin"
"flowstack/internal/database"
"flowstack/internal/model"
)
// argoApps 模拟 Argo CD 应用状态(演示用)
var argoApps = []gin.H{
{"metadata": gin.H{"name": "aiops-backend"},
"spec": gin.H{"source": gin.H{"repoURL": "https://gitea.91ai.io/rlwj-lab/aiops-gitops.git", "path": "apps/aiops-backend", "targetRevision": "main"}, "destination": gin.H{"server": "https://k8s.prod", "namespace": "aiops"}},
"status": gin.H{"sync": gin.H{"status": "Synced"}, "health": gin.H{"status": "Healthy"}, "operationState": gin.H{"phase": "Succeeded"}}},
{"metadata": gin.H{"name": "aiops-web"},
"spec": gin.H{"source": gin.H{"repoURL": "https://gitea.91ai.io/rlwj-lab/aiops-gitops.git", "path": "apps/aiops-web", "targetRevision": "main"}, "destination": gin.H{"server": "https://k8s.prod", "namespace": "aiops"}},
"status": gin.H{"sync": gin.H{"status": "OutOfSync"}, "health": gin.H{"status": "Progressing"}, "operationState": gin.H{"phase": "Running"}}},
}
// ListGitOpsApps GET /api/gitops/apps
func ListGitOpsApps(c *gin.Context) {
var apps []model.Application
database.DB.Order("updated_at DESC").Find(&apps)
c.JSON(http.StatusOK, apps)
}
// ArgoList GET /api/gitops/argo/apps
func ArgoList(c *gin.Context) {
c.JSON(http.StatusOK, argoApps)
}
// ArgoSync POST /api/gitops/argo/sync {appName}
func ArgoSync(c *gin.Context) {
var body struct {
AppName string `json:"appName" binding:"required"`
}
if err := c.ShouldBindJSON(&body); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "appName required"})
return
}
c.JSON(http.StatusOK, gin.H{"success": true, "appName": body.AppName, "message": "Sync triggered"})
}
// ArgoResourceTree GET /api/gitops/argo/:appName/tree
func ArgoResourceTree(c *gin.Context) {
name := c.Param("appName")
c.JSON(http.StatusOK, gin.H{
"nodes": []gin.H{
{"uid": "1", "name": name + "-deployment", "kind": "Deployment", "namespace": "aiops", "status": "healthy", "resourceVersion": "v1"},
{"uid": "2", "name": name + "-svc", "kind": "Service", "namespace": "aiops", "status": "healthy", "resourceVersion": "v1"},
{"uid": "3", "name": name + "-hpa", "kind": "HorizontalPodAutoscaler", "namespace": "aiops", "status": "healthy", "resourceVersion": "v2"},
},
"edges": []gin.H{{"source": "1", "target": "2"}, {"source": "1", "target": "3"}},
})
}
// ArgoDiff GET /api/gitops/argo/:appName/diff
func ArgoDiff(c *gin.Context) {
name := c.Param("appName")
c.JSON(http.StatusOK, gin.H{
"diffs": []gin.H{
{"kind": "Deployment", "name": name, "namespace": "aiops", "diff": "- image: registry.example.com/aiops/" + name + ":202610261130\n+ image: registry.example.com/aiops/" + name + ":202610271200", "status": "modified"},
},
})
}