feat: 发布权限 RBAC + PipelineEditor 修复 + 菜单优化

- 新增 DeployRBAC 页面: Jenkins-style Item Role 权限管理
  - 正则匹配流水线 code,实时命中预览
  - 内置用户分配面板,无需跳转系统管理
  - 支持创建/编辑/删除/启用禁用 Item Role
- 修复 PipelineEditor 成员管理 http 双解包 bug
- 修复 PipelineManager 前端 code 路由迁移(delete/trigger/stop/loadRuns)
- 新增批量删除流水线运行记录 API
- Sidebar: 应用发布下新增「发布权限」, 仓库认证→发布配置, 移除用户组
- 恢复系统管理菜单(不受发布权限改动影响)
- 后端 ItemRole CRUD handler + 正则匹配中间件已就绪
This commit is contained in:
attacker committed 2026-06-21 05:26:01 +00:00
1 parent 0fea0ccc20
commit 24f955832e
40 files changed
+7530 -1032

No files matched your search

+93 -7
View File
@@ -16,6 +16,9 @@ import (
// Register 注册所有路由 // Logan.Li
// cache 参数为可选 Redis 缓存实例(支持 redka/redis 双后端)
func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, cache interface{}) {
// 提取 Redis 缓存实例(供流水线 WebSocket 等使用)
redisCache, _ := cache.(ezredis.Cache)
// API版本前缀
api := r.Group("/api/v1")
@@ -64,6 +67,16 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
metricsWSHandler := handlers.NewMetricsWSHandler(metricsHub)
r.GET("/ws/metrics", middleware.JWTAuth(&cfg.JWT), metricsWSHandler.ServeWS)
// WebSocket 流水线日志推流
wsLogHandler := handlers.NewWSPipelineLogHandler(db, redisCache)
r.GET("/ws/pipeline/:runId/log", wsLogHandler.StreamLog)
r.GET("/api/v1/pipelines/runs/:runId/log-stream", wsLogHandler.GetLogHistory)
// 工作区文件浏览器(浏览/下载流水线构建目录文件)
workspaceFilesHandler := handlers.NewWorkspaceFilesHandler(db)
r.GET("/api/v1/pipelines/runs/:runId/files", workspaceFilesHandler.ListFiles)
r.GET("/api/v1/pipelines/runs/:runId/files/download", workspaceFilesHandler.DownloadFile)
// WebSocket K8s exec 交互终端 // Logan.Li
k8sHandler := handlers.NewKubernetesHandler(db)
r.GET("/ws/kubernetes-exec/:id/namespaces/:namespace/pods/:podName", middleware.JWTAuth(&cfg.JWT), k8sHandler.ExecTerminal)
@@ -77,6 +90,14 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
api.POST("/auth/login", authHandler.Login)
api.POST("/auth/register", authHandler.Register)
// Webhook 触发路由(无需 JWT,由 Webhook Token 签名验证)
pipelineHandler := handlers.NewPipelineHandler(db, redisCache)
webhookGroup := api.Group("/webhook")
{
webhookGroup.POST("/job/:jobName", pipelineHandler.WebhookTrigger)
webhookGroup.POST("/pipeline/:code", pipelineHandler.WebhookTrigger)
}
// Agent 安装处理器(在 servers 和 agent 路由中共享)
agentInstallHandler := handlers.NewAgentInstallHandler(db)
@@ -113,6 +134,16 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
// 权限列表(供角色权限选择)
admin.GET("/permissions", authHandler.ListPermissions)
// Item Role 管理(Jenkins-style regex pipeline matching) // Logan.Li
itemRoleHandler := handlers.NewItemRoleHandler(db)
admin.GET("/item-roles", itemRoleHandler.ListItemRoles)
admin.POST("/item-roles", itemRoleHandler.CreateItemRole)
admin.PUT("/item-roles/:id", itemRoleHandler.UpdateItemRole)
admin.DELETE("/item-roles/:id", itemRoleHandler.DeleteItemRole)
admin.GET("/users/:id/item-roles", itemRoleHandler.ListUserItemRoles)
admin.POST("/users/:id/item-roles", itemRoleHandler.AssignItemRole)
admin.DELETE("/users/:id/item-roles/:roleId", itemRoleHandler.RemoveItemRole)
}
// LDAP 配置路由
@@ -371,16 +402,51 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
}
// 流水线路由
pipelineHandler := handlers.NewPipelineHandler(db)
pipelines := authorized.Group("/pipelines")
{
pipelines.GET("", pipelineHandler.ListPipelines)
pipelines.POST("", pipelineHandler.CreatePipeline)
pipelines.GET("/:id", pipelineHandler.GetPipeline)
pipelines.PUT("/:id", pipelineHandler.UpdatePipeline)
pipelines.DELETE("/:id", pipelineHandler.DeletePipeline)
pipelines.POST("/:id/trigger", pipelineHandler.TriggerPipeline)
pipelines.GET("/:id/runs", pipelineHandler.ListRuns)
// 需要流水线权限的路由
pipelineScoped := pipelines.Group("/:code")
pipelineScoped.Use(middleware.PipelineAccess(db))
{
pipelineScoped.GET("", pipelineHandler.GetPipeline)
pipelineScoped.PUT("", pipelineHandler.UpdatePipeline)
pipelineScoped.DELETE("", pipelineHandler.DeletePipeline)
pipelineScoped.POST("/trigger", pipelineHandler.TriggerPipeline)
pipelineScoped.GET("/runs", pipelineHandler.ListRuns)
pipelineScoped.POST("/runs/:runId/stop", pipelineHandler.StopPipelineRun)
pipelineScoped.POST("/runs/batch-delete", pipelineHandler.BatchDeleteRuns)
pipelineScoped.GET("/runs/:runId/log", pipelineHandler.GetRunLog)
// 成员管理
pipelineScoped.GET("/members", pipelineHandler.ListPipelineMembers)
pipelineScoped.POST("/members", pipelineHandler.AddPipelineMember)
pipelineScoped.DELETE("/members/:memberId", pipelineHandler.RemovePipelineMember)
// 组授权
pipelineScoped.POST("/groups", pipelineHandler.AssignGroupToPipeline)
}
}
// 用户组管理路由
groups := authorized.Group("/deploy/groups")
{
groups.GET("", pipelineHandler.ListGroups)
groups.POST("", pipelineHandler.CreateGroup)
groups.PUT("/:id", pipelineHandler.UpdateGroup)
groups.DELETE("/:id", pipelineHandler.DeleteGroup)
groups.GET("/:id/members", pipelineHandler.ListGroupMembers)
groups.POST("/:id/members", pipelineHandler.AddGroupMember)
groups.DELETE("/:id/members/:memberId", pipelineHandler.RemoveGroupMember)
}
// 环境管理路由
environments := authorized.Group("/deploy/environments")
{
environments.GET("", pipelineHandler.ListEnvironments)
environments.POST("", pipelineHandler.CreateEnvironment)
environments.PUT("/:id", pipelineHandler.UpdateEnvironment)
environments.DELETE("/:id", pipelineHandler.DeleteEnvironment)
}
// 应用发布路由
@@ -394,6 +460,24 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
releases.POST("/:id/rollback", pipelineHandler.RollbackRelease)
}
// 仓库认证 & 镜像仓库认证路由
deployHandler := handlers.NewDeployHandler(db)
{
repos := authorized.Group("/deploy/repos")
repos.GET("", deployHandler.ListRepos)
repos.POST("", deployHandler.CreateRepo)
repos.GET("/:id", deployHandler.GetRepo)
repos.PUT("/:id", deployHandler.UpdateRepo)
repos.DELETE("/:id", deployHandler.DeleteRepo)
registries := authorized.Group("/deploy/registries")
registries.GET("", deployHandler.ListRegistries)
registries.POST("", deployHandler.CreateRegistry)
registries.GET("/:id", deployHandler.GetRegistry)
registries.PUT("/:id", deployHandler.UpdateRegistry)
registries.DELETE("/:id", deployHandler.DeleteRegistry)
}
// 自动化运维路由
automationHandler := handlers.NewAutomationHandler(db)
cronJobs := authorized.Group("/cron-jobs")
@@ -515,7 +599,6 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
}
// Agent 上报路由(无需 JWT)
redisCache, _ := cache.(ezredis.Cache)
agentHandler := handlers.NewAgentHandler(db, redisCache, metricsHub)
taskHandler := handlers.NewAgentTaskHandler(db)
agentGroup := api.Group("/agent")
@@ -645,8 +728,11 @@ func Register(r *gin.Engine, db *gorm.DB, log *zap.Logger, cfg *config.Config, c
credGroup := authorized.Group("/credentials")
{
credGroup.GET("", credentialHandler.ListCredentials)
credGroup.GET("/tags", credentialHandler.GetTags)
credGroup.POST("", credentialHandler.CreateCredential)
credGroup.GET("/:id", credentialHandler.GetCredential)
credGroup.PUT("/:id", credentialHandler.UpdateCredential)
credGroup.PATCH("/:id", credentialHandler.PatchCredential)
credGroup.DELETE("/:id", credentialHandler.DeleteCredential)
}
// 服务器凭据关联 // Logan.Li