- 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)
68 lines
2.7 KiB
Go
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"},
|
|
},
|
|
})
|
|
}
|