Files
opsxsql/backend/internal/handler/ghost.go
T
logan 622923821b feat: Go后端补全所有缺失API接口,消除前端404报错
新增接口:
- GET /instances/accessible — 查询页实例列表(按access_type筛选)
- GET /instances/:id/databases — 数据库列表(placeholder)
- GET /instances/:id/databases/:db/tables — 表列表(placeholder)
- POST /instances/test-connection — 新实例连接测试
- CRUD /instance-groups — 完整实例组管理(创建/更新/删除/查询单个)
- /instance-groups/:id/instances — 实例分配/移除
- /instance-groups/:id/users — 用户分配
- PUT /instance-groups/users/:userId/groups — 用户组更新
- GET/PUT /ldap-config — LDAP配置读写
- POST /ldap-config/test — LDAP连接测试(placeholder)
- POST /ldap-config/ou-list — OU列表(placeholder)
- POST /ldap-config/user-list — 用户列表(placeholder)
- PUT /settings — 批量更新设置
- GET /settings/cleanup/preview — 清理预览
- POST /settings/cleanup/run — 执行清理

修复:
- ListSettings 返回按group分组的dict(前端期望格式)
- ListInstanceGroups 返回enriched数据(含实例/用户计数)
- 前端所有页面0个404错误,查询页实例下拉框正常显示
2026-05-25 22:14:01 +08:00

165 lines
4.2 KiB
Go

package handler
import (
"net/http"
"time"
"sql-platform/config"
"sql-platform/internal/ghost"
"sql-platform/internal/model"
"github.com/gin-gonic/gin"
)
// POST /api/v1/ghost/dry-run
func GhOstDryRun(c *gin.Context) {
var task ghost.GhOstTask
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
task.DryRun = true
task.Execute = false
result, err := ghost.RunGhOst(c.Request.Context(), task, config.C.GhOstPath)
if err != nil {
c.JSON(http.StatusInternalServerError, result)
return
}
c.JSON(http.StatusOK, result)
}
// POST /api/v1/ghost/execute
func GhOstExecute(c *gin.Context) {
var task ghost.GhOstTask
if err := c.ShouldBindJSON(&task); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
task.DryRun = false
task.Execute = true
taskID := ghost.RunGhOstAsync(task, config.C.GhOstPath, nil)
c.JSON(http.StatusAccepted, gin.H{
"task_id": taskID,
"message": "gh-ost execution started",
})
}
// GET /api/v1/ghost/status/:taskId
func GhOstStatus(c *gin.Context) {
taskID := c.Param("taskId")
status, result := ghost.Manager.GetTaskStatus(taskID)
if status == "" {
c.JSON(http.StatusNotFound, gin.H{"detail": "Task not found"})
return
}
c.JSON(http.StatusOK, gin.H{
"task_id": taskID,
"status": status,
"result": result,
})
}
// GET /api/v1/ghost/tasks
func GhOstListTasks(c *gin.Context) {
tasks := ghost.Manager.ListTasks()
c.JSON(http.StatusOK, tasks)
}
// GET /api/v1/audit-rules
func ListAuditRules(c *gin.Context) {
var rules []model.AuditRule
model.DB.Order("id").Find(&rules)
c.JSON(http.StatusOK, rules)
}
// GET /api/v1/announcements
func ListAnnouncements(c *gin.Context) {
var announcements []model.Announcement
model.DB.Where("is_published = ?", true).Order("id DESC").Find(&announcements)
c.JSON(http.StatusOK, announcements)
}
// GET /api/v1/query-histories
func ListQueryHistories(c *gin.Context) {
_, _, _ = c.GetInt("user_id"), c.GetString("username"), c.GetString("role")
var histories []model.QueryHistory
model.DB.Order("id DESC").Limit(100).Find(&histories)
c.JSON(http.StatusOK, histories)
}
// GET /api/v1/settings — returns settings grouped by `group` field
func ListSettings(c *gin.Context) {
var settings []model.SystemSetting
model.DB.Order("sort_order").Find(&settings)
// group by `group` field
grouped := make(map[string][]model.SystemSetting)
for _, s := range settings {
grouped[s.Group] = append(grouped[s.Group], s)
}
c.JSON(http.StatusOK, grouped)
}
// PUT /api/v1/settings — batch update settings
func UpdateSettings(c *gin.Context) {
var items []struct {
ID int `json:"id"`
Value string `json:"value"`
}
if err := c.ShouldBindJSON(&items); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"detail": err.Error()})
return
}
for _, item := range items {
model.DB.Model(&model.SystemSetting{}).Where("id = ?", item.ID).Update("value", item.Value)
}
c.JSON(http.StatusOK, gin.H{"message": "Settings updated"})
}
// GET /api/v1/settings/cleanup/preview
func SettingsCleanupPreview(c *gin.Context) {
c.JSON(http.StatusOK, map[string]interface{}{})
}
// POST /api/v1/settings/cleanup/run
func SettingsCleanupRun(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "Cleanup done (placeholder)", "stats": map[string]interface{}{}})
}
// GET /api/v1/notifications/channels
func ListNotificationChannels(c *gin.Context) {
var channels []model.NotificationChannel
model.DB.Order("id").Find(&channels)
c.JSON(http.StatusOK, channels)
}
// GET /api/v1/notifications/templates
func ListNotificationTemplates(c *gin.Context) {
var templates []model.NotificationTemplate
model.DB.Order("id").Find(&templates)
c.JSON(http.StatusOK, templates)
}
// GET /api/v1/workflows
func ListWorkflows(c *gin.Context) {
var workflows []model.WorkflowDefinition
model.DB.Order("id").Find(&workflows)
c.JSON(http.StatusOK, workflows)
}
// GET /api/v1/instance-groups
func ListInstanceGroups(c *gin.Context) {
var groups []model.InstanceGroup
model.DB.Order("id").Find(&groups)
c.JSON(http.StatusOK, groups)
}
// POST /api/v1/notifications/test
func TestNotification(c *gin.Context) {
_ = time.Now()
c.JSON(http.StatusOK, gin.H{"message": "Notification sent (placeholder)"})
}