package handlers import ( "net/http" "strconv" "time" "github.com/gin-gonic/gin" "gorm.io/gorm" "everypublish/server/internal/api/response" "everypublish/server/internal/models" ) // ChallengeHandler 二次验证挑战处理器 type ChallengeHandler struct { DB *gorm.DB } // List 挑战列表(过期软置 expired) func (h *ChallengeHandler) List(c *gin.Context) { h.DB.Model(&models.Challenge{}). Where("workspace_id = ? and status = ? and expires_at < ?", c.GetUint64("wsid"), "active", time.Now()). Update("status", "expired") q := h.DB.Model(&models.Challenge{}).Where("workspace_id = ?", c.GetUint64("wsid")) if s := c.Query("status"); s != "" { q = q.Where("status = ?", s) } if a := c.Query("accountId"); a != "" { q = q.Where("account_id = ?", a) } var list []models.Challenge q.Order("id desc").Limit(100).Find(&list) response.OK(c, gin.H{"list": list}) } // Solve 人工完成挑战(验证码/APP确认;扫码由 Agent 完成) func (h *ChallengeHandler) Solve(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.Fail(c, http.StatusBadRequest, 1001, "参数不合法") return } var req struct { Value string `json:"value"` } _ = c.ShouldBindJSON(&req) var challenge models.Challenge if err = h.DB.Where("id = ? and workspace_id = ?", id, c.GetUint64("wsid")).First(&challenge).Error; err != nil { response.Fail(c, http.StatusNotFound, 1004, "挑战不存在") return } if challenge.Status != "active" { response.Fail(c, http.StatusConflict, 3003, "挑战已结束") return } now := time.Now() if err = h.DB.Model(&challenge).Updates(map[string]interface{}{"status": "solved", "solved_at": &now}).Error; err != nil { response.Fail(c, http.StatusInternalServerError, 5000, "更新失败") return } response.Audit(c, "challenge.solve", "challenge:"+itoa(id), gin.H{"kind": challenge.Kind}) response.OK(c, nil) } // Resend 一键重发(重置过期时间,实际重发出 Agent 执行) func (h *ChallengeHandler) Resend(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.Fail(c, http.StatusBadRequest, 1001, "参数不合法") return } var challenge models.Challenge if err = h.DB.Where("id = ? and workspace_id = ?", id, c.GetUint64("wsid")).First(&challenge).Error; err != nil { response.Fail(c, http.StatusNotFound, 1004, "挑战不存在") return } if challenge.Status != "expired" && challenge.Status != "suspended" { response.Fail(c, http.StatusConflict, 3003, "当前状态不可重发") return } if err = h.DB.Model(&challenge).Updates(map[string]interface{}{ "status": "active", "expires_at": time.Now().Add(30 * time.Minute), }).Error; err != nil { response.Fail(c, http.StatusInternalServerError, 5000, "更新失败") return } response.Audit(c, "challenge.resend", "challenge:"+itoa(id), nil) response.OK(c, nil) } // Suspend 挂起(超时/人工) func (h *ChallengeHandler) Suspend(c *gin.Context) { id, err := strconv.ParseUint(c.Param("id"), 10, 64) if err != nil { response.Fail(c, http.StatusBadRequest, 1001, "参数不合法") return } var challenge models.Challenge if err = h.DB.Where("id = ? and workspace_id = ?", id, c.GetUint64("wsid")).First(&challenge).Error; err != nil { response.Fail(c, http.StatusNotFound, 1004, "挑战不存在") return } if challenge.Status != "active" { response.Fail(c, http.StatusConflict, 3003, "挑战已结束") return } if err = h.DB.Model(&challenge).Update("status", "suspended").Error; err != nil { response.Fail(c, http.StatusInternalServerError, 5000, "更新失败") return } response.Audit(c, "challenge.suspend", "challenge:"+itoa(id), nil) response.OK(c, nil) }