- Agent CLI: 多轮对话、SSH 长连接、流式推理、Evolver 按用户隔离、历史侧栏 - 终端审计: 回放播放器、命令书签、筛选分页导出、命令输出统计、录制落盘修复 - 修复 session_commands 表迁移、Kill 竞态导致录制丢失、无录制会话回放提示 - 审计历史页只读(移除删除),移除回放弹框录制搜索
1056 lines
48 KiB
Go
1056 lines
48 KiB
Go
package router
|
||
|
||
import (
|
||
"ezops/controller"
|
||
"ezops/middleware"
|
||
"ezops/config"
|
||
"ezops/model"
|
||
"ezops/service"
|
||
ezredis "ezops/pkg/redis"
|
||
"ezops/pkg/toolinstaller"
|
||
"ezops/pkg/wshub"
|
||
"fmt"
|
||
"strconv"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
"go.uber.org/zap"
|
||
"gorm.io/gorm"
|
||
"time"
|
||
)
|
||
|
||
// 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")
|
||
|
||
// 健康检查
|
||
r.GET("/health", func(c *gin.Context) {
|
||
c.JSON(200, gin.H{"status": "ok"})
|
||
})
|
||
|
||
// Agent 二进制下载 + 一键安装脚本(无需鉴权,方便 curl|bash)
|
||
r.Static("/agent/bin", "./agent/bin")
|
||
r.StaticFile("/agent/install.sh", "./agent/deploy/install.sh")
|
||
r.StaticFile("/agent/aiops-agent-linux-amd64", "./agent/bin/aiops-agent-linux-amd64")
|
||
r.StaticFile("/agent/aiops-agent-linux-arm64", "./agent/bin/aiops-agent-linux-arm64")
|
||
r.StaticFile("/agent/aiops-agent-darwin-arm64", "./agent/bin/aiops-agent-darwin-arm64") // macOS Apple Silicon // Logan.Li
|
||
r.GET("/agent/arch", func(c *gin.Context) {
|
||
// 动态构建安装命令 URL(根据请求来源,兼容 nginx 反代)
|
||
scheme := "http"
|
||
if c.Request.TLS != nil {
|
||
scheme = "https"
|
||
}
|
||
if proto := c.GetHeader("X-Forwarded-Proto"); proto != "" {
|
||
scheme = proto
|
||
}
|
||
host := c.Request.Host
|
||
if host == "" {
|
||
host = "localhost:8080"
|
||
}
|
||
base := fmt.Sprintf("%s://%s", scheme, host)
|
||
c.JSON(200, gin.H{
|
||
"version": "1.0.0",
|
||
"downloads": gin.H{
|
||
"linux-amd64": "/agent/aiops-agent-linux-amd64",
|
||
"linux-arm64": "/agent/aiops-agent-linux-arm64",
|
||
"darwin-arm64": "/agent/aiops-agent-darwin-arm64",
|
||
},
|
||
"install": fmt.Sprintf("curl -fsSL %s/agent/install.sh | sudo bash -s -- --server %s --token <token>", base, base),
|
||
})
|
||
})
|
||
|
||
// === A3: 统一 WebSocket 连接管理器 === // Logan.Li
|
||
// 所有 WebSocket handler 共享同一个 WSManager(心跳、连接注册、监控、优雅关闭)
|
||
wsMgr := wshub.New(wshub.DefaultConfig())
|
||
|
||
// WebSocket终端 (JWT 鉴权,支持 ?token= 查询参数;终端会话录制) // Logan.Li
|
||
auditHub := controller.NewAuditHub(wsMgr)
|
||
webShellHandler := controller.NewWebShellHandler(db, auditHub, wsMgr)
|
||
r.GET("/ws/terminal", middleware.JWTAuth(&cfg.JWT, db), webShellHandler.Terminal)
|
||
|
||
// WebSocket 审计实时流 — 在线会话实时查看 // Logan.Li
|
||
r.GET("/ws/audit/:sessionId", middleware.JWTAuth(&cfg.JWT, db), func(c *gin.Context) {
|
||
idStr := c.Param("sessionId")
|
||
id, err := strconv.ParseUint(idStr, 10, 64)
|
||
if err != nil {
|
||
c.JSON(400, gin.H{"error": "invalid session id"})
|
||
return
|
||
}
|
||
var session model.TerminalSession
|
||
if err := db.First(&session, id).Error; err != nil {
|
||
c.JSON(404, gin.H{"error": "session not found"})
|
||
return
|
||
}
|
||
auditHub.ServeAuditStream(c, uint(id), session.RecordData, gin.H{
|
||
"session_id": session.ID,
|
||
"username": session.Username,
|
||
"server_name": session.ServerName,
|
||
"server_ip": session.ServerIP,
|
||
"start_time": session.StartTime,
|
||
"status": session.Status,
|
||
})
|
||
})
|
||
|
||
// WebSocket 实时指标推送(JWT 鉴权) // Logan.Li
|
||
metricsHub := controller.NewMetricsHub()
|
||
metricsWSHandler := controller.NewMetricsWSHandler(metricsHub, wsMgr)
|
||
r.GET("/ws/metrics", middleware.JWTAuth(&cfg.JWT, db), metricsWSHandler.ServeWS)
|
||
|
||
// WebSocket 流水线日志推流
|
||
wsLogHandler := controller.NewWSPipelineLogHandler(db, redisCache, wsMgr)
|
||
r.GET("/ws/pipeline/:runId/log", wsLogHandler.StreamLog)
|
||
r.GET("/api/v1/pipelines/runs/:runId/log-stream", wsLogHandler.GetLogHistory)
|
||
|
||
// 工作区文件浏览器(浏览/下载流水线构建目录文件)
|
||
workspaceFilesHandler := controller.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 := controller.NewKubernetesHandler(db, wsMgr)
|
||
r.GET("/ws/kubernetes-exec/:id/namespaces/:namespace/pods/:podName", middleware.JWTAuth(&cfg.JWT, db), k8sHandler.ExecTerminal)
|
||
|
||
// ToolInstaller(工具懒加载服务 — kubectl/maven/node/helm)// Logan.Li
|
||
ti := toolinstaller.New(db)
|
||
|
||
// V2 Services 层 — 依赖注入 (server, credential, alert) // Logan.Li
|
||
svcs := service.NewServices(db, redisCache, cfg.JWT.Secret)
|
||
|
||
// WebSocket kubectl 命令行终端 // Logan.Li
|
||
kubectlShellHandler := controller.NewKubectlShellHandler(db, ti, wsMgr)
|
||
r.GET("/ws/kubectl", middleware.JWTAuth(&cfg.JWT, db), kubectlShellHandler.Terminal)
|
||
|
||
// === A3: WebSocket 连接监控端点 === // Logan.Li
|
||
wsAPI := api.Group("/ws", middleware.JWTAuth(&cfg.JWT, db))
|
||
{
|
||
// GET /api/v1/ws/connections — 列出所有活跃 WS 连接
|
||
wsAPI.GET("/connections", func(c *gin.Context) {
|
||
connections := wsMgr.ListConnections()
|
||
c.JSON(200, gin.H{
|
||
"code": 200,
|
||
"data": connections,
|
||
})
|
||
})
|
||
// GET /api/v1/ws/stats — 聚合统计(总数 + 按类型/用户分布)
|
||
wsAPI.GET("/stats", func(c *gin.Context) {
|
||
c.JSON(200, gin.H{
|
||
"code": 200,
|
||
"data": wsMgr.GetStats(),
|
||
})
|
||
})
|
||
}
|
||
|
||
// 公开路由
|
||
authHandler := controller.NewUserHandler(db, cfg)
|
||
api.POST("/auth/login", authHandler.Login)
|
||
api.POST("/auth/register", authHandler.Register)
|
||
|
||
// Webhook 触发路由(无需 JWT,由 Webhook Token 签名验证)
|
||
pipelineHandler := controller.NewPipelineHandler(db, redisCache, ti)
|
||
webhookGroup := api.Group("/webhook")
|
||
{
|
||
webhookGroup.POST("/job/:jobName", pipelineHandler.WebhookTrigger)
|
||
webhookGroup.POST("/pipeline/:code", pipelineHandler.WebhookTrigger)
|
||
}
|
||
|
||
// Agent 安装处理器(在 servers 和 agent 路由中共享)
|
||
agentInstallHandler := controller.NewAgentInstallHandler(db)
|
||
|
||
// 需要认证的路由
|
||
authorized := api.Group("")
|
||
authorized.Use(middleware.JWTAuth(&cfg.JWT, db))
|
||
|
||
// 用户路由
|
||
user := authorized.Group("/user")
|
||
{
|
||
user.GET("/profile", authHandler.GetProfile)
|
||
user.GET("/permissions", authHandler.GetMyPermissions)
|
||
user.PUT("/profile", authHandler.UpdateProfile)
|
||
user.POST("/change-password", authHandler.ChangePassword)
|
||
user.PUT("/password", authHandler.ChangePassword) // 兼容前端 // Logan.Li
|
||
user.GET("/roles", authHandler.ListRoles)
|
||
user.GET("/api-access-key", authHandler.GetApiAccessKey)
|
||
user.PUT("/api-access-key", authHandler.UpdateApiAccessKey)
|
||
user.POST("/api-access-key/regenerate", authHandler.RegenerateApiAccessKey)
|
||
user.POST("/api-access-key/extend", authHandler.ExtendApiAccessKey)
|
||
user.DELETE("/api-access-key", authHandler.RevokeApiAccessKey)
|
||
}
|
||
|
||
// 管理员路由
|
||
admin := authorized.Group("/admin")
|
||
admin.Use(middleware.AdminAuth())
|
||
{
|
||
admin.GET("/users", authHandler.ListUsers)
|
||
admin.GET("/users/:id", authHandler.GetUser)
|
||
admin.POST("/users", authHandler.CreateUser)
|
||
admin.PUT("/users/:id", authHandler.UpdateUser)
|
||
admin.DELETE("/users/:id", authHandler.DeleteUser)
|
||
admin.POST("/users/:id/reset-password", authHandler.ResetUserPassword)
|
||
admin.POST("/users/batch-import", authHandler.BatchImportUsers)
|
||
admin.GET("/users/import-template", authHandler.ImportTemplate)
|
||
|
||
// 角色管理路由 // Logan.Li
|
||
admin.GET("/roles", authHandler.ListRoles)
|
||
admin.POST("/roles", authHandler.CreateRole)
|
||
admin.PUT("/roles/:id", authHandler.UpdateRole)
|
||
admin.DELETE("/roles/:id", authHandler.DeleteRole)
|
||
|
||
// 权限列表(供角色权限选择)
|
||
admin.GET("/permissions", authHandler.ListPermissions)
|
||
|
||
// Item Role 管理(Jenkins-style regex pipeline matching) // Logan.Li
|
||
itemRoleHandler := controller.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 配置路由
|
||
ldapHandler := controller.NewLDAPHandler(db)
|
||
ldapGroup := authorized.Group("/admin/ldap-config")
|
||
ldapGroup.Use(middleware.AdminAuth())
|
||
{
|
||
ldapGroup.GET("", ldapHandler.GetLDAPConfig)
|
||
ldapGroup.PUT("", ldapHandler.UpdateLDAPConfig)
|
||
ldapGroup.POST("/test", ldapHandler.TestLDAPConnection)
|
||
ldapGroup.POST("/users", ldapHandler.ListLDAPUsers)
|
||
ldapGroup.POST("/import", ldapHandler.BatchImportLDAP)
|
||
ldapGroup.POST("/ou-list", ldapHandler.ListLDAPOUs)
|
||
ldapGroup.POST("/create-ou", ldapHandler.CreateLDAPOU)
|
||
ldapGroup.POST("/create-user", ldapHandler.CreateLDAPUser)
|
||
ldapGroup.POST("/delete-user", ldapHandler.DeleteLDAPUser)
|
||
ldapGroup.POST("/update-user", ldapHandler.UpdateLDAPUser)
|
||
}
|
||
|
||
// 仪表盘路由
|
||
dashboardHandler := controller.NewDashboardHandler(db, redisCache)
|
||
dashboard := authorized.Group("/dashboard")
|
||
{
|
||
dashboard.GET("/stats", dashboardHandler.GetStats)
|
||
dashboard.GET("/recent-alerts", dashboardHandler.GetRecentAlerts)
|
||
dashboard.GET("/recent-releases", dashboardHandler.GetRecentReleases)
|
||
dashboard.GET("/metrics", dashboardHandler.GetMetrics)
|
||
}
|
||
|
||
// 服务器路由
|
||
serverHandlerV2 := controller.NewServerHandlerV2(svcs.Server, db)
|
||
servers := authorized.Group("/servers")
|
||
{
|
||
servers.GET("", serverHandlerV2.ListServers)
|
||
servers.GET("/stats", serverHandlerV2.GetServerStats)
|
||
servers.GET("/:id", serverHandlerV2.GetServer)
|
||
servers.POST("", serverHandlerV2.CreateServer)
|
||
servers.PUT("/:id", serverHandlerV2.UpdateServer)
|
||
servers.DELETE("/:id", serverHandlerV2.DeleteServer)
|
||
servers.POST("/batch-delete", serverHandlerV2.BatchDeleteServers)
|
||
servers.POST("/batch", serverHandlerV2.BatchCreateServers)
|
||
|
||
// Agent 远程安装 // Logan.Li
|
||
servers.POST("/:id/install-agent", agentInstallHandler.InstallAgent)
|
||
servers.POST("/install-agent", agentInstallHandler.BatchInstallAgent)
|
||
|
||
// SFTP 文件浏览 // Logan.Li
|
||
sftpHandler := controller.NewSFTPHandler(db)
|
||
servers.GET("/:id/files", sftpHandler.ListFiles)
|
||
servers.GET("/:id/files/download", sftpHandler.DownloadFile)
|
||
servers.POST("/:id/files/upload", sftpHandler.UploadFile)
|
||
servers.DELETE("/:id/files", sftpHandler.DeleteFile)
|
||
servers.POST("/:id/files/mkdir", sftpHandler.Mkdir)
|
||
}
|
||
|
||
// 云账号路由
|
||
cloudAccountHandler := controller.NewCloudAccountHandler(db)
|
||
cloudAccounts := authorized.Group("/cloud-accounts")
|
||
{
|
||
cloudAccounts.GET("", cloudAccountHandler.ListCloudAccounts)
|
||
cloudAccounts.POST("", cloudAccountHandler.CreateCloudAccount)
|
||
cloudAccounts.PUT("/:id", cloudAccountHandler.UpdateCloudAccount)
|
||
cloudAccounts.DELETE("/:id", cloudAccountHandler.DeleteCloudAccount)
|
||
|
||
// 云同步路由 — 区域同步 / 主机发现 / 导入主机 // Logan.Li
|
||
syncHandler := controller.NewCloudSyncHandler(db)
|
||
cloudAccounts.POST("/:id/sync-regions", syncHandler.SyncRegions)
|
||
cloudAccounts.GET("/:id/regions", syncHandler.GetAccountRegions)
|
||
cloudAccounts.POST("/:id/discover-instances", syncHandler.DiscoverInstances)
|
||
cloudAccounts.POST("/:id/import-instances", syncHandler.ImportInstances)
|
||
}
|
||
|
||
// 域名路由
|
||
domainHandler := controller.NewDomainHandler(db)
|
||
domains := authorized.Group("/domains")
|
||
{
|
||
domains.GET("", domainHandler.ListDomains)
|
||
domains.POST("", domainHandler.CreateDomain)
|
||
domains.PUT("/:id", domainHandler.UpdateDomain)
|
||
domains.DELETE("/:id", domainHandler.DeleteDomain)
|
||
}
|
||
|
||
// 云平台路由
|
||
platformHandler := controller.NewPlatformHandler(db)
|
||
platforms := authorized.Group("/platforms")
|
||
{
|
||
platforms.GET("", platformHandler.ListPlatforms)
|
||
platforms.POST("", platformHandler.CreatePlatform)
|
||
platforms.PUT("/:id", platformHandler.UpdatePlatform)
|
||
platforms.DELETE("/:id", platformHandler.DeletePlatform)
|
||
}
|
||
|
||
// 区域路由
|
||
regionHandler := controller.NewRegionHandler(db)
|
||
regions := authorized.Group("/regions")
|
||
{
|
||
regions.GET("", regionHandler.ListRegions)
|
||
regions.POST("", regionHandler.CreateRegion)
|
||
regions.PUT("/:id", regionHandler.UpdateRegion)
|
||
regions.DELETE("/batch-delete", regionHandler.BatchDeleteRegions) // Logan.Li
|
||
regions.DELETE("/:id", regionHandler.DeleteRegion)
|
||
}
|
||
|
||
// 云账单路由
|
||
billingHandler := controller.NewBillingHandler(db)
|
||
billings := authorized.Group("/cloud-billings")
|
||
{
|
||
billings.GET("", billingHandler.ListBillings)
|
||
billings.POST("", billingHandler.CreateBilling)
|
||
billings.POST("/batch", billingHandler.BatchCreateBillings)
|
||
billings.DELETE("/:id", billingHandler.DeleteBilling)
|
||
billings.GET("/stats", billingHandler.GetBillingStats)
|
||
billings.GET("/providers", billingHandler.GetProviderOptions)
|
||
}
|
||
billingAlerts := authorized.Group("/billing-alerts")
|
||
{
|
||
billingAlerts.GET("", billingHandler.ListAlertRules)
|
||
billingAlerts.POST("", billingHandler.CreateAlertRule)
|
||
billingAlerts.PUT("/:id", billingHandler.UpdateAlertRule)
|
||
billingAlerts.DELETE("/:id", billingHandler.DeleteAlertRule)
|
||
billingAlerts.POST("/:id/toggle", billingHandler.ToggleAlertRule)
|
||
}
|
||
|
||
// 资源模型路由
|
||
resourceModelHandler := controller.NewResourceModelHandler(db)
|
||
resourceModels := authorized.Group("/resource-models")
|
||
{
|
||
resourceModels.GET("", resourceModelHandler.ListResourceModels)
|
||
resourceModels.POST("", resourceModelHandler.CreateResourceModel)
|
||
resourceModels.PUT("/:id", resourceModelHandler.UpdateResourceModel)
|
||
resourceModels.DELETE("/:id", resourceModelHandler.DeleteResourceModel)
|
||
}
|
||
|
||
// ============================================================================
|
||
// 集群管理路由 (cluster CRUD + sync + topology — 合法 ID 路由)
|
||
// 资源列举路由 (nodes/pods/deployments/services/...) 保留向后兼容,
|
||
// 但应优先使用 /kubernetes/:clusterCode/... (Kuboard code 路由)。详见 ROADMAP A1。
|
||
// ============================================================================
|
||
clusters := authorized.Group("/kubernetes-clusters")
|
||
{
|
||
clusters.GET("", k8sHandler.ListClusters)
|
||
clusters.GET("/:id", k8sHandler.GetCluster)
|
||
clusters.GET("/:id/detail", k8sHandler.GetClusterDetail) // 编辑用详情(含 has_token 标记) // Logan.Li
|
||
clusters.POST("", k8sHandler.CreateCluster)
|
||
clusters.PUT("/:id", k8sHandler.UpdateCluster)
|
||
clusters.DELETE("/:id", k8sHandler.DeleteCluster)
|
||
clusters.POST("/:id/sync", k8sHandler.SyncCluster) // 同步集群资源 // Logan.Li
|
||
clusters.GET("/:id/nodes", k8sHandler.ListNodes) // Deprecated: use GET /kubernetes/:clusterCode/nodes
|
||
clusters.GET("/:id/nodes/:name", k8sHandler.GetNodeDetail) // M10 节点详情 // Logan.Li
|
||
clusters.GET("/:id/pods", k8sHandler.ListPods) // Deprecated: use GET /kubernetes/:clusterCode/pods
|
||
clusters.GET("/:id/deployments", k8sHandler.ListDeployments) // Deprecated: use GET /kubernetes/:clusterCode/deployments
|
||
clusters.GET("/:id/services", k8sHandler.ListServices) // Deprecated: use GET /kubernetes/:clusterCode/services
|
||
clusters.GET("/:id/ingresses", k8sHandler.ListIngresses) // Deprecated: use GET /kubernetes/:clusterCode/ingresses
|
||
clusters.GET("/:id/topology", k8sHandler.GetTopology) // 获取拓扑布局 // Logan.Li
|
||
clusters.PUT("/:id/topology", k8sHandler.SaveTopology) // 保存拓扑布局 // Logan.Li
|
||
|
||
// Pod 日志 // Logan.Li
|
||
clusters.GET("/:id/namespaces/:namespace/pods/:podName/logs", k8sHandler.GetPodLogs)
|
||
// Pod 终端 exec (命令执行 / 文件浏览) // Logan.Li
|
||
clusters.POST("/:id/namespaces/:namespace/pods/:podName/exec", k8sHandler.ExecInPod)
|
||
// Deployment 扩缩容 & 重启 // Logan.Li
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/scale", k8sHandler.ScaleDeployment)
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/restart", k8sHandler.RestartDeployment)
|
||
// Deployment 暂停/恢复滚动更新 // Logan.Li M5
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/pause", k8sHandler.PauseDeployment)
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/resume", k8sHandler.ResumeDeployment)
|
||
// Deployment CI 集成 (Webhook 更新镜像) // Logan.Li
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/update-image", k8sHandler.UpdateDeploymentImage)
|
||
// HPA 管理 // Logan.Li
|
||
clusters.GET("/:id/hpas", k8sHandler.ListHPAs)
|
||
clusters.POST("/:id/hpas", k8sHandler.CreateHPA)
|
||
clusters.DELETE("/:id/namespaces/:namespace/hpas/:name", k8sHandler.DeleteHPA)
|
||
// CronJob 管理 // Logan.Li
|
||
clusters.GET("/:id/cronjobs", k8sHandler.ListCronJobs)
|
||
clusters.POST("/:id/cronjobs", k8sHandler.CreateCronJob)
|
||
clusters.POST("/:id/namespaces/:namespace/cronjobs/:name/suspend", k8sHandler.SuspendCronJob)
|
||
clusters.POST("/:id/namespaces/:namespace/cronjobs/:name/trigger", k8sHandler.TriggerCronJob)
|
||
clusters.DELETE("/:id/namespaces/:namespace/cronjobs/:name", k8sHandler.DeleteCronJob)
|
||
// ReplicaSet 列表 / Deployment 回滚 / 历史版本 // Logan.Li
|
||
clusters.GET("/:id/namespaces/:namespace/replicasets", k8sHandler.ListReplicaSetsNS)
|
||
clusters.GET("/:id/namespaces/:namespace/deployments/:name/revisions", k8sHandler.ListDeploymentRevisionsNS)
|
||
clusters.POST("/:id/namespaces/:namespace/deployments/:name/rollback", k8sHandler.RollbackDeploymentNS)
|
||
// Pod 文件浏览 / 下载 / 删除 // Logan.Li
|
||
clusters.POST("/:id/namespaces/:namespace/pods/:podName/files/list", k8sHandler.ListPodFilesNS)
|
||
clusters.GET("/:id/namespaces/:namespace/pods/:podName/files/download", k8sHandler.DownloadPodFileNS)
|
||
clusters.POST("/:id/namespaces/:namespace/pods/:podName/files/upload", k8sHandler.UploadPodFileNS)
|
||
clusters.DELETE("/:id/namespaces/:namespace/pods/:podName", k8sHandler.DeletePodNS)
|
||
// Namespace // Logan.Li
|
||
clusters.GET("/:id/namespaces", k8sHandler.ListNamespaces)
|
||
}
|
||
|
||
// ============================================================================
|
||
// Kuboard-style namespace-scoped routes (/kubernetes/{clusterCode}/namespace/{ns}/...)
|
||
// ============================================================================
|
||
kubernetesRouter := authorized.Group("/kubernetes")
|
||
{
|
||
// Namespace overview
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace", func(c *gin.Context) {
|
||
k8sHandler.ListDeploymentsNS(c)
|
||
})
|
||
|
||
// Node detail (cluster-scoped, not namespace-scoped) // M10
|
||
kubernetesRouter.GET("/:clusterCode/nodes/:name", k8sHandler.GetNodeDetail)
|
||
// Cluster-scoped node list (canonical — replaces /kubernetes-clusters/:id/nodes)
|
||
kubernetesRouter.GET("/:clusterCode/nodes", k8sHandler.ListNodes)
|
||
|
||
// Deployments
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/deployments", k8sHandler.ListDeploymentsNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/deployments/:name", k8sHandler.GetDeploymentNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/scale", k8sHandler.ScaleDeploymentNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/restart", k8sHandler.RestartDeploymentNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/pause", k8sHandler.PauseDeploymentNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/resume", k8sHandler.ResumeDeploymentNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/update-image", k8sHandler.UpdateDeploymentImageNS)
|
||
|
||
// Pods
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/pods", k8sHandler.ListPodsNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/pods/:podName/logs", k8sHandler.GetPodLogsNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/pods/:podName/exec", k8sHandler.ExecInPodNS)
|
||
|
||
// Services
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/services", k8sHandler.ListServicesNS)
|
||
|
||
// Ingresses
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/ingresses", k8sHandler.ListIngressesNS)
|
||
|
||
// IngressClass 列表(可视化编辑 Ingress 时选择 ingressClassName 用) // Logan.Li
|
||
kubernetesRouter.GET("/:clusterCode/ingressclasses", k8sHandler.ListIngressClasses)
|
||
|
||
// HPA
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/hpas", k8sHandler.ListHPAsNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/hpas", k8sHandler.CreateHPANS)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespace/:namespace/hpas/:name", k8sHandler.DeleteHPANS)
|
||
|
||
// CronJob
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/cronjobs", k8sHandler.ListCronJobsNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/cronjobs", k8sHandler.CreateCronJobNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/cronjobs/:name/suspend", k8sHandler.SuspendCronJobNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/cronjobs/:name/trigger", k8sHandler.TriggerCronJobNS)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespace/:namespace/cronjobs/:name", k8sHandler.DeleteCronJobNS)
|
||
|
||
// 🆕 ReplicaSet 列表 // Logan.Li
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/replicasets", k8sHandler.ListReplicaSetsNS)
|
||
// 🆕 Deployment 回滚 / 发布历史 // Logan.Li
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/deployments/:name/revisions", k8sHandler.ListDeploymentRevisionsNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/deployments/:name/rollback", k8sHandler.RollbackDeploymentNS)
|
||
// 🆕 Pod 文件浏览 / 下载 / 删除 // Logan.Li
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/pods/:podName/files/list", k8sHandler.ListPodFilesNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/pods/:podName/files/download", k8sHandler.DownloadPodFileNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/pods/:podName/files/upload", k8sHandler.UploadPodFileNS)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespace/:namespace/pods/:podName", k8sHandler.DeletePodNS)
|
||
|
||
// ConfigMap / Secret
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/configmaps", k8sHandler.ListConfigMaps)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/secrets", k8sHandler.ListSecrets)
|
||
|
||
// Namespace list
|
||
kubernetesRouter.GET("/:clusterCode/namespaces", k8sHandler.ListNamespaces)
|
||
// 🆕 M14: Namespace 管理看板 (detail + CRUD)
|
||
kubernetesRouter.GET("/:clusterCode/namespaces/detail", k8sHandler.GetNamespacesDetail)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/detail", k8sHandler.GetNamespaceDetailHandler)
|
||
kubernetesRouter.POST("/:clusterCode/namespaces", k8sHandler.CreateNamespaceHandler)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespaces/:namespace", k8sHandler.DeleteNamespaceHandler)
|
||
// All-namespace listing (no ns filter) — canonical code-based routes (A1)
|
||
kubernetesRouter.GET("/:clusterCode/deployments", k8sHandler.ListDeploymentsAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/pods", k8sHandler.ListPodsAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/hpas", k8sHandler.ListHPAsAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/services", k8sHandler.ListServicesAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/ingresses", k8sHandler.ListIngressesAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/cronjobs", k8sHandler.ListCronJobsAllNS)
|
||
|
||
// 🆕 Pod Metrics (kubectl top pods)
|
||
kubernetesRouter.GET("/:clusterCode/metrics/pods", k8sHandler.GetPodMetrics)
|
||
// 🆕 Events
|
||
kubernetesRouter.GET("/:clusterCode/events", k8sHandler.ListEvents)
|
||
kubernetesRouter.GET("/:clusterCode/events/summary", k8sHandler.GetEventsSummary)
|
||
// 🆕 Capacity Analysis
|
||
kubernetesRouter.GET("/:clusterCode/capacity", k8sHandler.GetCapacityAnalysis)
|
||
// 🆕 YAML Apply
|
||
kubernetesRouter.POST("/:clusterCode/yaml/apply", k8sHandler.ApplyYAML)
|
||
// 🆕 批量导出资源 (Deployment+Service+Ingress+ConfigMap+Secret) // Logan.Li
|
||
kubernetesRouter.GET("/:clusterCode/export", k8sHandler.ExportResourcesAllNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/export", k8sHandler.ExportResourcesNS)
|
||
|
||
// PVC (存储)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/pvcs", k8sHandler.ListPVCsNS)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespace/:namespace/pvcs/:name", k8sHandler.DeletePVCNS)
|
||
|
||
// RBAC — ServiceAccount / Role / RoleBinding
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/serviceaccounts", k8sHandler.ListServiceAccountsNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/roles", k8sHandler.ListRolesNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/rolebindings", k8sHandler.ListRoleBindingsNS)
|
||
|
||
// StatefulSet / DaemonSet
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/statefulsets", k8sHandler.ListStatefulSetsNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/daemonsets", k8sHandler.ListDaemonSetsNS)
|
||
// StatefulSet operations (Kuboard code routes)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/statefulsets/:name", k8sHandler.GetStatefulSetDetailNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/statefulsets/:name/scale", k8sHandler.ScaleStatefulSetNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/statefulsets/:name/restart", k8sHandler.RestartStatefulSetNS)
|
||
// DaemonSet operations (Kuboard code routes)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/daemonsets/:name", k8sHandler.GetDaemonSetDetailNS)
|
||
kubernetesRouter.POST("/:clusterCode/namespace/:namespace/daemonsets/:name/restart", k8sHandler.RestartDaemonSetNS)
|
||
|
||
// StatefulSet/DaemonSet YAML (specific routes to avoid Gin radix tree conflict with generic /:resource/:name/yaml)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/statefulsets/:name/yaml", k8sHandler.GetResourceYAMLNS)
|
||
kubernetesRouter.PUT("/:clusterCode/namespace/:namespace/statefulsets/:name/yaml", k8sHandler.UpdateResourceYAMLNS)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/daemonsets/:name/yaml", k8sHandler.GetResourceYAMLNS)
|
||
kubernetesRouter.PUT("/:clusterCode/namespace/:namespace/daemonsets/:name/yaml", k8sHandler.UpdateResourceYAMLNS)
|
||
|
||
// YAML view/edit (generic — works for resources without specific static routes)
|
||
kubernetesRouter.GET("/:clusterCode/namespace/:namespace/:resource/:name/yaml", k8sHandler.GetResourceYAMLNS)
|
||
kubernetesRouter.PUT("/:clusterCode/namespace/:namespace/:resource/:name/yaml", k8sHandler.UpdateResourceYAMLNS)
|
||
kubernetesRouter.DELETE("/:clusterCode/namespace/:namespace/:resource/:name/yaml", k8sHandler.DeleteResourceYAMLNS)
|
||
|
||
// 🆕 M13: 多集群资源对比看板
|
||
kubernetesRouter.GET("/compare", k8sHandler.GetMultiClusterComparison)
|
||
}
|
||
|
||
// 日志数据源 handler 需要提前初始化,以便注入告警引擎执行日志告警规则。// Logan.Li
|
||
logDSHandler := controller.NewLogDSHandler(db)
|
||
|
||
// 告警引擎(分布式锁 — 多副本部署时仅 Leader 执行检查)
|
||
alertEngine := controller.NewAlertEngine(db, redisCache, logDSHandler)
|
||
|
||
// 告警路由
|
||
alertHandler := controller.NewAlertHandler(db)
|
||
alerts := authorized.Group("/alerts")
|
||
{
|
||
alerts.GET("", alertHandler.ListAlerts)
|
||
alerts.GET("/rules", alertHandler.ListRules)
|
||
alerts.POST("/rules", alertHandler.CreateRule)
|
||
alerts.PUT("/rules/:id", alertHandler.UpdateRule)
|
||
alerts.DELETE("/rules/batch-delete", alertHandler.BatchDeleteRules)
|
||
alerts.DELETE("/rules/:id", alertHandler.DeleteRule)
|
||
alerts.POST("/rules/:id/toggle", alertHandler.ToggleRule)
|
||
alerts.POST("/:id/ack", alertHandler.AcknowledgeAlert)
|
||
alerts.GET("/notify-channels", alertHandler.ListNotifyChannels)
|
||
alerts.POST("/notify-channels", alertHandler.CreateNotifyChannel)
|
||
alerts.PUT("/notify-channels/:id", alertHandler.UpdateNotifyChannel)
|
||
alerts.DELETE("/notify-channels/:id", alertHandler.DeleteNotifyChannel)
|
||
alerts.POST("/notify-channels/:id/test", alertHandler.TestNotifyChannel)
|
||
alerts.POST("/quick-setup", alertHandler.QuickSetupRules)
|
||
// 报警联系人
|
||
alertContactV2 := controller.NewAlertContactHandlerV2(svcs.AlertContact)
|
||
alerts.GET("/contacts", alertContactV2.ListContacts)
|
||
alerts.POST("/contacts", alertContactV2.CreateContact)
|
||
alerts.PUT("/contacts/:id", alertContactV2.UpdateContact)
|
||
alerts.DELETE("/contacts/:id", alertContactV2.DeleteContact)
|
||
// 报警联系组
|
||
alertGroupV2 := controller.NewAlertContactGroupHandlerV2(svcs.AlertContactGroup)
|
||
alerts.GET("/contact-groups", alertGroupV2.ListGroups)
|
||
alerts.POST("/contact-groups", alertGroupV2.CreateGroup)
|
||
alerts.PUT("/contact-groups/:id", alertGroupV2.UpdateGroup)
|
||
alerts.DELETE("/contact-groups/:id", alertGroupV2.DeleteGroup)
|
||
// 告警静默规则
|
||
alerts.GET("/silences", alertHandler.ListSilences)
|
||
alerts.POST("/silences", alertHandler.CreateSilence)
|
||
alerts.PUT("/silences/:id", alertHandler.UpdateSilence)
|
||
alerts.DELETE("/silences/:id", alertHandler.DeleteSilence)
|
||
alerts.POST("/silences/:id/toggle", alertHandler.ToggleSilence)
|
||
// 告警引擎状态 — 查看当前 Leader 副本和分布式锁状态
|
||
alerts.GET("/engine/status", func(c *gin.Context) {
|
||
c.JSON(200, model.Response{Code: 200, Data: alertEngine.GetEngineStatus()})
|
||
})
|
||
}
|
||
|
||
// 流水线路由
|
||
pipelines := authorized.Group("/pipelines")
|
||
{
|
||
pipelines.GET("", pipelineHandler.ListPipelines)
|
||
pipelines.POST("", pipelineHandler.CreatePipeline)
|
||
|
||
// 需要流水线权限的路由
|
||
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("/queue", pipelineHandler.GetPipelineQueue)
|
||
pipelineScoped.DELETE("/queue/:runId", pipelineHandler.CancelQueueItem)
|
||
// 成员管理
|
||
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)
|
||
}
|
||
|
||
// 应用发布路由
|
||
releases := authorized.Group("/releases")
|
||
{
|
||
releases.GET("", pipelineHandler.ListReleases)
|
||
releases.POST("", pipelineHandler.CreateRelease)
|
||
releases.POST("/:id/approve", pipelineHandler.ApproveRelease)
|
||
releases.POST("/:id/reject", pipelineHandler.RejectRelease) // 驳回发布 // Logan.Li
|
||
releases.POST("/:id/deploy", pipelineHandler.DeployRelease)
|
||
releases.POST("/:id/rollback", pipelineHandler.RollbackRelease)
|
||
}
|
||
|
||
// 仓库认证 & 镜像仓库认证路由
|
||
deployHandler := controller.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)
|
||
}
|
||
|
||
// 工具配置路由(发布配置 Tab ④)// Logan.Li
|
||
toolConfigHandler := controller.NewToolConfigHandler(db, ti)
|
||
tools := authorized.Group("/deploy/tools")
|
||
{
|
||
tools.GET("", toolConfigHandler.ListTools)
|
||
tools.GET("/:id", toolConfigHandler.GetTool)
|
||
tools.PUT("/:id", toolConfigHandler.UpdateTool)
|
||
tools.POST("/:id/install", toolConfigHandler.InstallTool)
|
||
tools.POST("/:id/check", toolConfigHandler.CheckToolStatus)
|
||
tools.GET("/:id/versions", toolConfigHandler.GetToolVersions)
|
||
}
|
||
|
||
// 自动化运维路由
|
||
automationHandler := controller.NewAutomationHandler(db)
|
||
cronJobs := authorized.Group("/cron-jobs")
|
||
{
|
||
cronJobs.GET("", automationHandler.ListCronJobs)
|
||
cronJobs.POST("", automationHandler.CreateCronJob)
|
||
cronJobs.GET("/:id", automationHandler.GetCronJob)
|
||
cronJobs.PUT("/:id", automationHandler.UpdateCronJob)
|
||
cronJobs.DELETE("/batch-delete", automationHandler.BatchDeleteCronJobs)
|
||
cronJobs.DELETE("/:id", automationHandler.DeleteCronJob)
|
||
cronJobs.POST("/:id/toggle", automationHandler.ToggleCronJob)
|
||
cronJobs.POST("/:id/execute", automationHandler.ExecuteCronJob)
|
||
}
|
||
|
||
scripts := authorized.Group("/scripts")
|
||
{
|
||
scripts.GET("", automationHandler.ListScripts)
|
||
scripts.POST("", automationHandler.CreateScript)
|
||
scripts.GET("/:id", automationHandler.GetScript)
|
||
scripts.PUT("/:id", automationHandler.UpdateScript)
|
||
scripts.POST("/batch-delete", automationHandler.BatchDeleteScripts)
|
||
scripts.DELETE("/:id", automationHandler.DeleteScript)
|
||
scripts.POST("/:id/execute", automationHandler.ExecuteScript)
|
||
}
|
||
|
||
// 远程执行路由 — Go 原生 SSH 批量执行替代 Ansible // Logan.Li
|
||
remoteExecHandler := controller.NewRemoteExecHandler(db)
|
||
remoteExec := authorized.Group("/remote-exec")
|
||
{
|
||
remoteExec.POST("/execute", remoteExecHandler.BatchExec)
|
||
remoteExec.POST("/upload", remoteExecHandler.BatchUpload)
|
||
remoteExec.POST("/upload-file", remoteExecHandler.UploadFile)
|
||
remoteExec.GET("/hosts", remoteExecHandler.MatchHosts)
|
||
remoteExec.POST("/server/:id/exec", remoteExecHandler.ExecOnServer)
|
||
}
|
||
|
||
// 任务执行记录(脚本/定时任务执行历史,不含 Ansible)
|
||
authorized.GET("/task-executions", automationHandler.ListExecutions)
|
||
|
||
// 日志监控路由 // Logan.Li
|
||
logHandler := controller.NewLogHandler(db)
|
||
logProxyHandler := controller.NewLogProxyHandler(logDSHandler)
|
||
logs := authorized.Group("/logs")
|
||
{
|
||
logs.GET("", logHandler.QueryLogs)
|
||
logs.GET("/sources", logHandler.ListSources)
|
||
logs.POST("/sources", logHandler.CreateSource)
|
||
logs.DELETE("/sources/:id", logHandler.DeleteSource)
|
||
logs.POST("/loki/query", logProxyHandler.QueryLoki)
|
||
logs.POST("/elasticsearch/query", logProxyHandler.QueryES)
|
||
}
|
||
logDSGroup := authorized.Group("/log-datasources")
|
||
{
|
||
logDSGroup.GET("", logDSHandler.List)
|
||
logDSGroup.GET("/indices", logDSHandler.ListIndices)
|
||
logDSGroup.GET("/:id", logDSHandler.Get)
|
||
logDSGroup.POST("", logDSHandler.Create)
|
||
logDSGroup.PUT("/:id", logDSHandler.Update)
|
||
logDSGroup.DELETE("/:id", logDSHandler.Delete)
|
||
logDSGroup.POST("/:id/default", logDSHandler.SetDefault)
|
||
logDSGroup.POST("/test", logDSHandler.TestConnection)
|
||
}
|
||
|
||
// 审批流程路由
|
||
approvalHandler := controller.NewApprovalHandler(db)
|
||
approvals := authorized.Group("/approvals")
|
||
{
|
||
approvals.GET("/templates", approvalHandler.ListTemplates)
|
||
approvals.POST("/templates", approvalHandler.CreateTemplate)
|
||
approvals.GET("/templates/:id", approvalHandler.GetTemplate)
|
||
approvals.PUT("/templates/:id", approvalHandler.UpdateTemplate)
|
||
approvals.DELETE("/templates/:id", approvalHandler.DeleteTemplate)
|
||
|
||
approvals.GET("/processes", approvalHandler.ListProcesses)
|
||
approvals.POST("/processes", approvalHandler.CreateProcess)
|
||
approvals.GET("/processes/:id", approvalHandler.GetProcess)
|
||
approvals.POST("/processes/:id/approve", approvalHandler.ApproveProcess)
|
||
approvals.POST("/processes/:id/reject", approvalHandler.RejectProcess)
|
||
}
|
||
|
||
// 审批流程配置(工作流定义)
|
||
workflowHandler := controller.NewWorkflowHandler(db)
|
||
workflows := authorized.Group("/approval-workflows")
|
||
{
|
||
workflows.GET("", workflowHandler.ListWorkflows)
|
||
workflows.POST("", workflowHandler.CreateWorkflow)
|
||
workflows.GET("/:id", workflowHandler.GetWorkflow)
|
||
workflows.PUT("/:id", workflowHandler.UpdateWorkflow)
|
||
workflows.DELETE("/:id", workflowHandler.DeleteWorkflow)
|
||
workflows.PUT("/:id/default", workflowHandler.SetDefaultWorkflow)
|
||
workflows.GET("/:id/steps", workflowHandler.GetWorkflowSteps)
|
||
}
|
||
|
||
// 导航路由
|
||
navHandler := controller.NewNavigationHandler(db)
|
||
navigations := authorized.Group("/navigations")
|
||
{
|
||
navigations.GET("/groups", navHandler.ListGroups)
|
||
navigations.POST("/groups", navHandler.CreateGroup)
|
||
navigations.PUT("/groups/:id", navHandler.UpdateGroup)
|
||
navigations.DELETE("/groups/:id", navHandler.DeleteGroup)
|
||
|
||
navigations.GET("/links", navHandler.ListLinks)
|
||
navigations.POST("/links", navHandler.CreateLink)
|
||
navigations.PUT("/links/:id", navHandler.UpdateLink)
|
||
navigations.DELETE("/links/:id", navHandler.DeleteLink)
|
||
navigations.POST("/links/:id/favorite", navHandler.ToggleFavorite)
|
||
}
|
||
|
||
// 审计日志路由
|
||
auditHandler := controller.NewAuditHandler(db)
|
||
audit := authorized.Group("/audit")
|
||
{
|
||
audit.GET("/logs", auditHandler.ListAuditLogs)
|
||
audit.GET("/logs/:id", auditHandler.GetAuditLogDetail)
|
||
audit.GET("/modules", auditHandler.GetAuditModules)
|
||
audit.GET("/actions", auditHandler.GetAuditActions)
|
||
audit.GET("/logs/export", auditHandler.ExportAuditLogs)
|
||
audit.GET("/statistics", auditHandler.GetAuditStatistics)
|
||
audit.POST("/logs/clear", auditHandler.ClearAuditLogs)
|
||
}
|
||
|
||
// 终端会话审计路由(WebSSH 录制回放) // Logan.Li
|
||
terminalSessionHandler := controller.NewTerminalSessionHandler(db, webShellHandler.KillSession)
|
||
ts := authorized.Group("/terminal-sessions")
|
||
{
|
||
ts.GET("", terminalSessionHandler.ListTerminalSessions)
|
||
ts.POST("/batch-delete", terminalSessionHandler.BatchDeleteTerminalSessions)
|
||
ts.GET("/:id", terminalSessionHandler.GetTerminalSession)
|
||
ts.GET("/:id/record", terminalSessionHandler.GetTerminalRecord)
|
||
ts.DELETE("/:id", terminalSessionHandler.DeleteTerminalSession)
|
||
ts.POST("/:id/kill", terminalSessionHandler.KillSession)
|
||
ts.GET("/:id/download", terminalSessionHandler.DownloadSession)
|
||
ts.POST("/:id/replay-view", terminalSessionHandler.LogReplayView)
|
||
ts.GET("/:id/stream", terminalSessionHandler.StreamSession)
|
||
}
|
||
|
||
// 会话命令审计路由(JumpServer 式命令审计) // Logan.Li
|
||
scHandler := controller.NewSessionCommandHandler(db)
|
||
sc := authorized.Group("/session-commands")
|
||
{
|
||
sc.GET("", scHandler.ListSessionCommands)
|
||
}
|
||
|
||
// 登录日志路由 // Logan.Li
|
||
llHandler := controller.NewLoginLogHandler(db)
|
||
ll := authorized.Group("/login-logs")
|
||
{
|
||
ll.GET("", llHandler.ListLoginLogs)
|
||
}
|
||
|
||
// Agent 上报路由(无需 JWT)
|
||
agentHandler := controller.NewAgentHandler(db, redisCache, metricsHub)
|
||
taskHandler := controller.NewAgentTaskHandler(db)
|
||
agentGroup := api.Group("/agent")
|
||
{
|
||
agentGroup.POST("/report", agentHandler.ReceiveReport)
|
||
agentGroup.GET("/tasks/poll", taskHandler.PollTasks)
|
||
agentGroup.POST("/tasks/:task_id/result", taskHandler.SubmitResult)
|
||
agentGroup.POST("/aggregate", agentHandler.AggregateMetrics)
|
||
agentGroup.POST("/exec", agentInstallHandler.RemoteExec)
|
||
// 健康探针:Agent 拉取本机配置(无需 JWT)
|
||
agentGroup.GET("/health-probes/config", agentHandler.GetHealthProbesForAgent)
|
||
// 端口策略:Agent 拉取本机端口过滤策略(无需 JWT)
|
||
agentGroup.GET("/port-policy", agentHandler.GetPortPolicyForAgent)
|
||
}
|
||
// Agent 数据查询 + 任务管理(需要 JWT)
|
||
agentQuery := authorized.Group("/agent")
|
||
{
|
||
agentQuery.GET("/latest", agentHandler.GetLatestReports)
|
||
agentQuery.GET("/history/:hostname", agentHandler.GetHostHistory)
|
||
agentQuery.GET("/aggregate", agentHandler.GetAggregate)
|
||
|
||
// 远程任务
|
||
agentQuery.POST("/tasks", taskHandler.CreateTask)
|
||
agentQuery.GET("/tasks", taskHandler.ListTasks)
|
||
agentQuery.GET("/tasks/:id", taskHandler.GetTask)
|
||
|
||
// 告警规则
|
||
agentQuery.GET("/rules", taskHandler.ListRules)
|
||
agentQuery.POST("/rules/quick-setup", taskHandler.QuickSetupRules)
|
||
agentQuery.POST("/rules", taskHandler.CreateRule)
|
||
agentQuery.PUT("/rules/:id", taskHandler.UpdateRule)
|
||
agentQuery.DELETE("/rules/:id", taskHandler.DeleteRule)
|
||
|
||
// 告警事件
|
||
agentQuery.GET("/alerts", taskHandler.ListAlerts)
|
||
agentQuery.POST("/alerts/:id/resolve", taskHandler.ResolveAlert)
|
||
|
||
// Agent 安装/卸载
|
||
agentQuery.POST("/uninstall", agentInstallHandler.UninstallAgent)
|
||
agentQuery.POST("/batch-uninstall", agentInstallHandler.BatchUninstallAgent)
|
||
agentQuery.POST("/batch-upgrade", agentInstallHandler.BatchUpgradeAgent)
|
||
|
||
// 健康探针管理(CRUD)
|
||
agentQuery.GET("/health-probes", agentHandler.ListHealthProbes)
|
||
agentQuery.POST("/health-probes", agentHandler.CreateHealthProbe)
|
||
agentQuery.PUT("/health-probes/:id", agentHandler.UpdateHealthProbe)
|
||
agentQuery.DELETE("/health-probes/:id", agentHandler.DeleteHealthProbe)
|
||
|
||
// 端口策略(CRUD)
|
||
agentQuery.GET("/port-policies/:hostname", agentHandler.GetPortPolicy)
|
||
agentQuery.PUT("/port-policies/:hostname", agentHandler.SavePortPolicy)
|
||
}
|
||
|
||
// Agent 二进制下载(无需鉴权,Agent 升级时使用)
|
||
r.Static("/api/agent/bin", "/app/agent/bin")
|
||
|
||
// AI 智能助手 - LLM 配置 + 对话代理(OpenAI 兼容) // Logan.Li
|
||
llmHandler := controller.NewLLMHandler(db)
|
||
aiopsGroup := authorized.Group("/aiops")
|
||
{
|
||
aiopsGroup.GET("/llm-config", llmHandler.GetLLMConfig)
|
||
aiopsGroup.PUT("/llm-config", llmHandler.SaveLLMConfig)
|
||
aiopsGroup.POST("/llm-config/test", llmHandler.TestLLMConnection)
|
||
aiopsGroup.POST("/chat", llmHandler.Chat)
|
||
aiopsGroup.POST("/chat/stream", llmHandler.ChatStream)
|
||
aiopsGroup.GET("/chat-history", llmHandler.GetChatHistory)
|
||
aiopsGroup.DELETE("/chat-history", llmHandler.ClearChatHistory)
|
||
}
|
||
|
||
// 指标浏览器 — 监控指标元数据 + 时序查询 // Logan.Li
|
||
// Agent CLI — AI 运维 Agent(WebSocket 推流 + 会话历史回放) // Logan.Li
|
||
agentCLIHandler := controller.NewAgentCLIHandler(db)
|
||
r.GET("/ws/agent-cli", middleware.JWTAuth(&cfg.JWT, db), agentCLIHandler.Run)
|
||
agentCLIGroup := authorized.Group("/aiops/agent-cli")
|
||
{
|
||
agentCLIGroup.GET("/sessions", agentCLIHandler.ListSessions)
|
||
agentCLIGroup.GET("/sessions/:id", agentCLIHandler.GetSession)
|
||
agentCLIGroup.DELETE("/sessions/:id", agentCLIHandler.DeleteSession)
|
||
}
|
||
|
||
metricsHandler := controller.NewMetricsHandler(db)
|
||
metricsGroup := authorized.Group("/metrics")
|
||
{
|
||
metricsGroup.GET("/targets", metricsHandler.ListTargets)
|
||
metricsGroup.GET("/list", metricsHandler.ListMetrics)
|
||
metricsGroup.GET("/query", metricsHandler.QueryValues)
|
||
metricsGroup.GET("/names", metricsHandler.ListMetricNames)
|
||
metricsGroup.GET("/top", metricsHandler.TopHosts)
|
||
}
|
||
|
||
// 中间件监控 — Redis/MySQL 后端直连采集 // Logan.Li
|
||
mwHandler := controller.NewMiddlewareMonitorHandler(db)
|
||
mwGroup := authorized.Group("/middleware-monitors")
|
||
{
|
||
mwGroup.GET("", mwHandler.ListMiddlewareTargets)
|
||
mwGroup.POST("", mwHandler.CreateMiddlewareTarget)
|
||
mwGroup.GET("/:id", mwHandler.GetMiddlewareTarget)
|
||
mwGroup.PUT("/:id", mwHandler.UpdateMiddlewareTarget)
|
||
mwGroup.DELETE("/:id", mwHandler.DeleteMiddlewareTarget)
|
||
mwGroup.POST("/:id/test", mwHandler.TestMiddlewareConnection)
|
||
mwGroup.POST("/collect", mwHandler.CollectNow)
|
||
mwGroup.GET("/:id/metrics", mwHandler.GetMiddlewareMetrics)
|
||
}
|
||
mwHandler.StartCollector(30) // 每 30s 采集一次
|
||
|
||
// SNMP 设备管理 — 网络设备/服务器硬件 // Logan.Li
|
||
snmpHandler := controller.NewSNMPHandler(db)
|
||
snmpGroup := authorized.Group("/snmp")
|
||
{
|
||
snmpGroup.GET("/devices", snmpHandler.ListDevices)
|
||
snmpGroup.POST("/devices", snmpHandler.CreateDevice)
|
||
snmpGroup.PUT("/devices/:id", snmpHandler.UpdateDevice)
|
||
snmpGroup.DELETE("/devices/:id", snmpHandler.DeleteDevice)
|
||
snmpGroup.DELETE("/devices/batch-delete", snmpHandler.BatchDeleteDevices)
|
||
snmpGroup.GET("/templates", snmpHandler.ListTemplates)
|
||
snmpGroup.POST("/templates", snmpHandler.CreateTemplate)
|
||
snmpGroup.GET("/stats", snmpHandler.GetSNMPStats)
|
||
}
|
||
|
||
// Zabbix 代理 — 镜像 Zabbix API 为前端面板可用格式 // Logan.Li
|
||
zabbixHandler := controller.NewZabbixProxyHandler()
|
||
zabbixGroup := authorized.Group("/zabbix")
|
||
{
|
||
zabbixGroup.POST("/connect", zabbixHandler.Connect)
|
||
zabbixGroup.GET("/host/:hostid", zabbixHandler.GetHostDetail)
|
||
}
|
||
|
||
// Prometheus 代理 — 转发 PromQL 查询到 Prometheus 服务器 // Logan.Li
|
||
promHandler := controller.NewPrometheusProxyHandler()
|
||
promGroup := authorized.Group("/prometheus")
|
||
{
|
||
promGroup.POST("/connect", promHandler.Connect)
|
||
promGroup.POST("/query", promHandler.Query)
|
||
promGroup.POST("/query_range", promHandler.QueryRange)
|
||
promGroup.POST("/dashboard", promHandler.Dashboard)
|
||
}
|
||
|
||
// Prometheus 数据源管理 — CRUD + 连接测试 // Logan.Li
|
||
promDSHandler := controller.NewPromDSHandler(db)
|
||
promDSGroup := authorized.Group("/prometheus-datasources")
|
||
{
|
||
promDSGroup.GET("", promDSHandler.List)
|
||
promDSGroup.GET("/:id", promDSHandler.Get)
|
||
promDSGroup.POST("", promDSHandler.Create)
|
||
promDSGroup.PUT("/:id", promDSHandler.Update)
|
||
promDSGroup.DELETE("/:id", promDSHandler.Delete)
|
||
promDSGroup.POST("/:id/default", promDSHandler.SetDefault)
|
||
promDSGroup.POST("/test", promDSHandler.TestConnection)
|
||
}
|
||
|
||
// Zabbix 数据源管理 — CRUD + 连接测试 // Logan.Li
|
||
zabbixDSHandler := controller.NewZabbixDSHandler(db)
|
||
zabbixDSGroup := authorized.Group("/zabbix-datasources")
|
||
{
|
||
zabbixDSGroup.GET("", zabbixDSHandler.List)
|
||
zabbixDSGroup.GET("/:id", zabbixDSHandler.Get)
|
||
zabbixDSGroup.POST("", zabbixDSHandler.Create)
|
||
zabbixDSGroup.PUT("/:id", zabbixDSHandler.Update)
|
||
zabbixDSGroup.DELETE("/:id", zabbixDSHandler.Delete)
|
||
zabbixDSGroup.POST("/:id/default", zabbixDSHandler.SetDefault)
|
||
zabbixDSGroup.POST("/test", zabbixDSHandler.TestConnection)
|
||
}
|
||
|
||
// 自定义监控仪表盘 — CRUD + 设默认 // Logan.Li
|
||
customDashHandler := controller.NewCustomDashboardHandler(db)
|
||
customDashGroup := authorized.Group("/custom-dashboards")
|
||
{
|
||
customDashGroup.GET("", customDashHandler.List)
|
||
customDashGroup.GET("/:id", customDashHandler.Get)
|
||
customDashGroup.POST("", customDashHandler.Create)
|
||
customDashGroup.PUT("/:id", customDashHandler.Update)
|
||
customDashGroup.DELETE("/:id", customDashHandler.Delete)
|
||
customDashGroup.POST("/:id/default", customDashHandler.SetDefault)
|
||
}
|
||
|
||
// 链路追踪数据源管理 — CRUD + 测试连接 + 设默认 // Logan.Li
|
||
traceDSHandler := controller.NewTraceDSHandler(db)
|
||
traceProxyHandler := controller.NewTraceProxyHandler(traceDSHandler)
|
||
traceDSGroup := authorized.Group("/trace-datasources")
|
||
{
|
||
traceDSGroup.GET("", traceDSHandler.List)
|
||
traceDSGroup.GET("/:id", traceDSHandler.Get)
|
||
traceDSGroup.POST("", traceDSHandler.Create)
|
||
traceDSGroup.PUT("/:id", traceDSHandler.Update)
|
||
traceDSGroup.DELETE("/:id", traceDSHandler.Delete)
|
||
traceDSGroup.POST("/:id/default", traceDSHandler.SetDefault)
|
||
traceDSGroup.POST("/test", traceDSHandler.TestConnection)
|
||
}
|
||
// 链路追踪查询代理 — 转发 Jaeger/Tempo API
|
||
authorized.GET("/traces", traceProxyHandler.GetTraces)
|
||
authorized.GET("/traces/services", traceProxyHandler.GetServices)
|
||
authorized.GET("/traces/:traceId", traceProxyHandler.GetTraceDetail)
|
||
|
||
// 凭据管理 — SSH密钥/API Token/密码统一加密管理 // Logan.Li
|
||
credentialHandlerV2 := controller.NewCredentialHandlerV2(svcs.Credential)
|
||
credentialHandler := controller.NewCredentialHandler(db, cfg.JWT.Secret) // 仅用于 GetTags/PatchCredential/AssignCredential(待拆分)
|
||
credGroup := authorized.Group("/credentials")
|
||
{
|
||
credGroup.GET("", credentialHandlerV2.ListCredentials)
|
||
credGroup.GET("/tags", credentialHandler.GetTags)
|
||
credGroup.POST("", credentialHandlerV2.CreateCredential)
|
||
credGroup.GET("/:id", credentialHandlerV2.GetCredential)
|
||
credGroup.PUT("/:id", credentialHandlerV2.UpdateCredential)
|
||
credGroup.PATCH("/:id", credentialHandler.PatchCredential)
|
||
credGroup.DELETE("/:id", credentialHandlerV2.DeleteCredential)
|
||
}
|
||
// 服务器凭据关联 // Logan.Li
|
||
authorized.POST("/servers/:id/assign-credential", credentialHandler.AssignCredential)
|
||
|
||
// 站点拨测 — HTTP/TCP/DNS/PING 主动监控 // Logan.Li
|
||
siteHandler := controller.NewSiteHandler(db)
|
||
siteGroup := authorized.Group("/site")
|
||
{
|
||
siteGroup.GET("/tasks", siteHandler.ListTasks)
|
||
siteGroup.POST("/tasks", siteHandler.CreateTask)
|
||
siteGroup.PUT("/tasks/:id", siteHandler.UpdateTask)
|
||
siteGroup.DELETE("/tasks/:id", siteHandler.DeleteTask)
|
||
siteGroup.DELETE("/tasks/batch-delete", siteHandler.BatchDeleteTasks)
|
||
siteGroup.GET("/results", siteHandler.ListResults)
|
||
siteGroup.GET("/stats", siteHandler.GetSiteStats)
|
||
}
|
||
|
||
// 启动告警引擎(每 30 秒检查一次;alertEngine 在告警路由上方创建)
|
||
go func() {
|
||
ticker := time.NewTicker(30 * time.Second)
|
||
defer ticker.Stop()
|
||
for range ticker.C {
|
||
alertEngine.Check()
|
||
}
|
||
}()
|
||
}
|