package handler import ( "ckwk/internal/conf" "encoding/json" "fmt" "net/http" "time" "ckwk/internal/dto" "ckwk/pkg/log" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" ) var debugLogUpgrader = websocket.Upgrader{ CheckOrigin: func(r *http.Request) bool { return true }, } type debugConfigReq struct { Enabled bool `json:"enabled"` } func DebugConfig(ctx *gin.Context) { ctx.JSON(http.StatusOK, dto.Success(map[string]any{ "enabled": conf.IsRuntimeDebugEnabled(), "proxy": conf.DebugProxy, "skip_ssl_verify": conf.DebugSkipSSLVerify, "build_mode": conf.Mode, "proxy_configured": conf.DebugProxy != "", })) } func UpdateDebugConfig(ctx *gin.Context) { var req debugConfigReq if err := ctx.ShouldBindJSON(&req); err != nil { ctx.JSON(http.StatusBadRequest, dto.Error(400, "请求参数错误")) return } conf.SetRuntimeDebugEnabled(req.Enabled) DebugConfig(ctx) } func DebugLogs(ctx *gin.Context) { if !ensureDebugEnabled(ctx) { return } ctx.JSON(http.StatusOK, dto.Success(map[string]any{ "list": log.Entries(), })) } func DebugLogsDownload(ctx *gin.Context) { if !ensureDebugEnabled(ctx) { return } entries := log.Entries() content, err := json.MarshalIndent(entries, "", " ") if err != nil { ctx.JSON(http.StatusInternalServerError, dto.Error(500, "日志导出失败")) return } filename := fmt.Sprintf("wk-debug-logs-%s.json", time.Now().Format("20060102-150405")) ctx.Header("Content-Type", "application/json; charset=utf-8") ctx.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename)) ctx.Data(http.StatusOK, "application/json; charset=utf-8", content) } func DebugLogWS(ctx *gin.Context) { if !ensureDebugEnabled(ctx) { return } conn, err := debugLogUpgrader.Upgrade(ctx.Writer, ctx.Request, nil) if err != nil { return } defer conn.Close() subID, ch := log.Subscribe() defer log.Unsubscribe(subID) for _, entry := range log.Entries() { if err := conn.WriteJSON(entry); err != nil { return } } done := make(chan struct{}) go func() { defer close(done) for { if _, _, err := conn.ReadMessage(); err != nil { return } } }() ticker := time.NewTicker(30 * time.Second) defer ticker.Stop() for { select { case <-done: return case entry, ok := <-ch: if !ok { return } if err := conn.WriteJSON(entry); err != nil { return } case <-ticker.C: if err := conn.WriteMessage(websocket.PingMessage, []byte("ping")); err != nil { return } } } } func ensureDebugEnabled(ctx *gin.Context) bool { if conf.IsRuntimeDebugEnabled() { return true } ctx.JSON(http.StatusForbidden, dto.Error(403, "调试功能未开启,请先在设置页手动开启")) return false }