Files
opsxsql/backend/router/router.go
T
logan f2e93a09c0 feat: 细节打磨 - 全面修复核心功能
后端修复:
- ticket: 添加独立 CancelTicket/RollbackTicket/GetTicketTimeline/GetTicketApprovals handler
  (之前 cancel/rollback 都错误调用 ApproveTicket, timeline/approvals 调用 GetTicket)
- instance_detail: 7个 TODO placeholder 端点全部实现真实 MySQL 查询
  (databases/tables/db-schema/table-detail/processlist/status/users)
- instance_detail: ExecuteQuery 添加真实 Redis 命令执行(只读命令白名单)
- redis: 全部 handler 从 placeholder 改为真实 go-redis 实现
  (info/dbsize/keys/key-detail/slowlog/clients/command/create-key/delete-key)
- router: 添加 sql-assistant/format, sql-assistant/analyze, tickets/audit 路由
- router: 添加 dictionary/*, mongo/* stub 路由(前端不报 404)
- stub_handlers: 新增 SQL 审计/格式化/分析 handler + dictionary/mongo stub
- model: TicketStatistics.Completed JSON tag 改为 success 匹配前端
- ticket: ExecuteTicket 完成 status 改为 success 而非 completed

前端兼容:
- 所有前端 API 路径后端都有对应路由(无 404)
- TicketStatistics 返回 success 字段与前端 stats?.success 对齐
2026-05-26 20:40:45 +08:00

194 lines
8.0 KiB
Go

package router
import (
"sql-platform/internal/handler"
"sql-platform/internal/middleware"
"github.com/gin-gonic/gin"
)
func Setup(r *gin.Engine) {
api := r.Group("/api/v1")
// Public routes
api.POST("/auth/login", handler.Login)
// Authenticated routes
auth := api.Group("")
auth.Use(middleware.AuthMiddleware())
{
// Auth
auth.GET("/auth/me", handler.GetMe)
// Dashboard
auth.GET("/dashboard/stats", handler.DashboardStats)
auth.GET("/dashboard/my-tickets", handler.MyTickets)
// Tickets
auth.GET("/tickets/statistics", handler.TicketStatistics)
auth.GET("/tickets", handler.ListTickets)
auth.GET("/tickets/scheduled/list", handler.ListScheduledTasks)
auth.GET("/tickets/:id", handler.GetTicket)
auth.POST("/tickets", handler.CreateTicket)
auth.POST("/tickets/:id/approve", handler.ApproveTicket)
auth.POST("/tickets/:id/review", handler.ApproveTicket)
auth.POST("/tickets/:id/execute", handler.ExecuteTicket)
auth.POST("/tickets/:id/cancel-schedule", handler.CancelScheduledTask)
auth.POST("/tickets/:id/cancel", handler.CancelTicket)
auth.GET("/tickets/:id/timeline", handler.GetTicketTimeline)
auth.GET("/tickets/:id/approvals", handler.GetTicketApprovals)
auth.POST("/tickets/:id/rollback", handler.RollbackTicket)
// Instances
auth.GET("/instances", handler.ListInstances)
auth.GET("/instances/accessible", handler.AccessibleInstances)
auth.GET("/instances/stats", handler.InstanceStats)
auth.GET("/instances/:id", handler.GetInstance)
auth.GET("/instances/:id/databases", handler.ListDatabases)
auth.GET("/instances/:id/databases/:db/tables", handler.ListTables)
auth.POST("/instances/:id/test-connection", handler.TestConnection)
auth.POST("/instances/test-connection", handler.TestNewConnection)
// Instance Detail (databases/tables/schema/processlist/status/users)
auth.GET("/instances/:id/detail/databases", handler.DetailListDatabases)
auth.GET("/instances/:id/detail/tables", handler.DetailListTables)
auth.GET("/instances/:id/detail/db-schema", handler.DetailDbSchema)
auth.GET("/instances/:id/detail/table-detail", handler.DetailTableDetail)
auth.GET("/instances/:id/detail/processlist", handler.DetailProcesslist)
auth.GET("/instances/:id/detail/status", handler.DetailStatus)
auth.POST("/instances/:id/detail/processlist/:pid/kill", handler.DetailKillProcess)
auth.GET("/instances/:id/detail/users", handler.DetailListUsers)
// Instance management (admin/dba only)
admin := auth.Group("")
admin.Use(middleware.RequireRole("admin", "dba"))
{
admin.POST("/instances", handler.CreateInstance)
admin.PUT("/instances/:id", handler.UpdateInstance)
admin.DELETE("/instances/:id", handler.DeleteInstance)
}
// Instance Groups
auth.GET("/instance-groups", handler.ListInstanceGroups)
auth.GET("/instance-groups/:id", handler.GetInstanceGroup)
auth.GET("/instance-groups/:id/users", handler.ListGroupUsers)
auth.POST("/instance-groups/:id/instances", handler.AssignGroupInstances)
auth.DELETE("/instance-groups/:id/instances/:instanceId", handler.RemoveGroupInstance)
auth.POST("/instance-groups/:id/users", handler.AssignGroupUsers)
auth.PUT("/instance-groups/users/:userId/groups", handler.UpdateUserGroups)
// Instance Group management (admin/dba only)
igAdmin := auth.Group("")
igAdmin.Use(middleware.RequireRole("admin", "dba"))
{
igAdmin.POST("/instance-groups", handler.CreateInstanceGroup)
igAdmin.PUT("/instance-groups/:id", handler.UpdateInstanceGroup)
igAdmin.DELETE("/instance-groups/:id", handler.DeleteInstanceGroup)
}
// Redis
auth.GET("/redis/:id/info", handler.RedisInfo)
auth.GET("/redis/:id/dbsize", handler.RedisDbsize)
auth.GET("/redis/:id/keys", handler.RedisScanKeys)
auth.GET("/redis/:id/keys/:key", handler.RedisGetKeyDetail)
auth.GET("/redis/:id/slowlog", handler.RedisSlowlog)
auth.GET("/redis/:id/clients", handler.RedisClients)
auth.POST("/redis/:id/command", handler.RedisExecuteCommand)
auth.POST("/redis/:id/keys", handler.RedisCreateKey)
auth.DELETE("/redis/:id/keys/:key", handler.RedisDeleteKey)
// Users (admin only)
userAdmin := auth.Group("")
userAdmin.Use(middleware.RequireRole("admin"))
{
userAdmin.GET("/users", handler.ListUsers)
userAdmin.POST("/users", handler.CreateUser)
userAdmin.PUT("/users/:id", handler.UpdateUser)
userAdmin.DELETE("/users/:id", handler.DeleteUser)
userAdmin.POST("/users/:id/reset-password", handler.ResetUserPassword)
userAdmin.POST("/users/batch-import", handler.BatchImportUsers)
userAdmin.POST("/users/batch-import-ldap", handler.BatchImportLdapUsers)
userAdmin.GET("/users/import-template", handler.ImportTemplate)
}
// LDAP config (admin only)
ldapAdmin := auth.Group("")
ldapAdmin.Use(middleware.RequireRole("admin"))
{
ldapAdmin.GET("/ldap-config", handler.GetLDAPConfig)
ldapAdmin.PUT("/ldap-config", handler.UpdateLDAPConfig)
ldapAdmin.POST("/ldap-config/test", handler.TestLDAPConnection)
ldapAdmin.POST("/ldap-config/ou-list", handler.ListLDAPOUs)
ldapAdmin.POST("/ldap-config/user-list", handler.ListLDAPUsers)
ldapAdmin.POST("/ldap-config/create-ou", handler.CreateLDAPOU)
ldapAdmin.POST("/ldap-config/create-user", handler.CreateLDAPUser)
ldapAdmin.POST("/ldap-config/batch-import-ldap", handler.BatchImportLDAP)
ldapAdmin.POST("/ldap-config/delete-user", handler.DeleteLDAPUser)
}
// Settings
auth.GET("/settings", handler.ListSettings)
auth.PUT("/settings", handler.UpdateSettings)
auth.GET("/settings/cleanup/preview", handler.SettingsCleanupPreview)
auth.POST("/settings/cleanup/run", handler.SettingsCleanupRun)
// gh-ost (admin/dba only)
ghostGroup := auth.Group("")
ghostGroup.Use(middleware.RequireRole("admin", "dba"))
{
ghostGroup.POST("/ghost/dry-run", handler.GhOstDryRun)
ghostGroup.POST("/ghost/execute", handler.GhOstExecute)
ghostGroup.GET("/ghost/status/:taskId", handler.GhOstStatus)
ghostGroup.GET("/ghost/tasks", handler.GhOstListTasks)
}
// Audit Logs
auth.GET("/audit-logs", handler.ListAuditLogs)
// Query execution
auth.POST("/query/execute", handler.ExecuteQuery)
auth.POST("/query", handler.ExecuteQuery)
// SQL Assistant
auth.POST("/sql-assistant/format", handler.SQLFormat)
auth.POST("/sql-assistant/analyze", handler.SQLAnalyze)
// Ticket Audit
auth.POST("/tickets/audit", handler.AuditTicketSQL)
// Dictionary (stub)
auth.GET("/dictionary/instances/:instanceId/databases", handler.DictionaryDatabases)
auth.GET("/dictionary/instances/:instanceId/tables", handler.DictionaryTables)
auth.GET("/dictionary/instances/:instanceId/tables/:tableName", handler.DictionaryTableDetail)
auth.GET("/dictionary/instances/:instanceId/export", handler.DictionaryExport)
// MongoDB (stub)
auth.GET("/mongo/:id/databases", handler.MongoDatabases)
auth.GET("/mongo/:id/databases/:database/collections", handler.MongoCollections)
auth.GET("/mongo/:id/collections/:collection/stats", handler.MongoCollectionStats)
auth.POST("/mongo/:id/query", handler.MongoQuery)
auth.POST("/mongo/:id/execute", handler.MongoExecute)
auth.POST("/mongo/:id/test-connection", handler.MongoTestConnection)
// Read-only resources
auth.GET("/audit-rules", handler.ListAuditRules)
auth.GET("/announcements", handler.ListAnnouncements)
auth.GET("/query-histories", handler.ListQueryHistories)
// Notifications CRUD (replaces old channels/templates endpoints)
auth.GET("/notifications", handler.ListNotifications)
auth.POST("/notifications", handler.CreateNotification)
auth.PUT("/notifications/:id", handler.UpdateNotification)
auth.DELETE("/notifications/:id", handler.DeleteNotification)
auth.POST("/notifications/:id/test", handler.TestNotificationSend)
auth.GET("/workflows", handler.ListWorkflows)
auth.GET("/workflows/:id", handler.GetWorkflow)
auth.POST("/workflows", handler.CreateWorkflow)
auth.PUT("/workflows/:id", handler.UpdateWorkflow)
auth.DELETE("/workflows/:id", handler.DeleteWorkflow)
auth.POST("/workflows/:id/set-default", handler.SetDefaultWorkflow)
auth.POST("/workflows/:id/clone", handler.CloneWorkflow)
}
}