intelligent_test_handler.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. package admin
  2. import (
  3. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  4. "github.com/Wei-Shaw/sub2api/internal/server/middleware"
  5. "github.com/Wei-Shaw/sub2api/internal/service"
  6. "github.com/gin-gonic/gin"
  7. "net/http"
  8. "strconv"
  9. "time"
  10. )
  11. type IntelligentTestHandler struct {
  12. svc *service.IntelligentTestService
  13. }
  14. func NewIntelligentTestHandler(svc *service.IntelligentTestService) *IntelligentTestHandler {
  15. return &IntelligentTestHandler{svc: svc}
  16. }
  17. func intelligentAdminActor(c *gin.Context) (int64, bool) {
  18. subject, ok := middleware.GetAuthSubjectFromContext(c)
  19. if !ok || subject.UserID <= 0 {
  20. response.Unauthorized(c, "authentication required")
  21. return 0, false
  22. }
  23. role, ok := middleware.GetUserRoleFromContext(c)
  24. if !ok || (role != "admin" && role != "super_admin") {
  25. response.Forbidden(c, "administrator access required")
  26. return 0, false
  27. }
  28. return subject.UserID, true
  29. }
  30. func ParseIntelligentTestFilter(c *gin.Context) (service.IntelligentTestFilter, bool) {
  31. f := service.IntelligentTestFilter{Page: 1, PageSize: 24, Search: c.Query("search"), Platform: c.Query("platform"), AccountType: c.Query("account_type"), AccountStatus: c.Query("account_status"), TestType: c.Query("test_type"), Status: c.Query("status")}
  32. if f.AccountType == "" {
  33. f.AccountType = c.Query("type")
  34. }
  35. if len(f.Search) > 200 {
  36. response.BadRequest(c, "search is too long")
  37. return f, false
  38. }
  39. for key, dst := range map[string]*int{"page": &f.Page, "page_size": &f.PageSize} {
  40. if raw := c.Query(key); raw != "" {
  41. v, err := strconv.Atoi(raw)
  42. if err != nil || v < 1 || v > 1000000 {
  43. response.BadRequest(c, "invalid "+key)
  44. return f, false
  45. }
  46. *dst = v
  47. }
  48. }
  49. for key, dst := range map[string]*int64{"account_id": &f.AccountID, "group_id": &f.GroupID} {
  50. if raw := c.Query(key); raw != "" {
  51. v, err := strconv.ParseInt(raw, 10, 64)
  52. if err != nil || v < 1 {
  53. response.BadRequest(c, "invalid "+key)
  54. return f, false
  55. }
  56. *dst = v
  57. }
  58. }
  59. if raw := c.Query("anti_degradation"); raw != "" {
  60. v, err := strconv.ParseBool(raw)
  61. if err != nil {
  62. response.BadRequest(c, "invalid anti_degradation")
  63. return f, false
  64. }
  65. f.AntiDegradation = &v
  66. }
  67. if raw := c.Query("only_abnormal"); raw != "" {
  68. v, err := strconv.ParseBool(raw)
  69. if err != nil {
  70. response.BadRequest(c, "invalid only_abnormal")
  71. return f, false
  72. }
  73. f.OnlyAbnormal = v
  74. }
  75. for key, dst := range map[string]**time.Time{"from": &f.From, "to": &f.To} {
  76. if raw := c.Query(key); raw != "" {
  77. v, err := time.Parse(time.RFC3339, raw)
  78. if err != nil {
  79. response.BadRequest(c, "invalid "+key+"; use RFC3339")
  80. return f, false
  81. }
  82. *dst = &v
  83. }
  84. }
  85. if f.From != nil && f.To != nil && f.From.After(*f.To) {
  86. response.BadRequest(c, "from must be before to")
  87. return f, false
  88. }
  89. if f.PageSize > 100 {
  90. f.PageSize = 100
  91. }
  92. return f, true
  93. }
  94. func IntelligentTestParamID(c *gin.Context, param string) (int64, bool) {
  95. id, err := strconv.ParseInt(c.Param(param), 10, 64)
  96. if err != nil || id < 1 {
  97. response.BadRequest(c, "invalid identifier")
  98. return 0, false
  99. }
  100. return id, true
  101. }
  102. func (h *IntelligentTestHandler) Accounts(c *gin.Context) {
  103. actor, ok := intelligentAdminActor(c)
  104. if !ok {
  105. return
  106. }
  107. f, ok := ParseIntelligentTestFilter(c)
  108. if !ok {
  109. return
  110. }
  111. out, err := h.svc.Accounts(c.Request.Context(), actor, f)
  112. if !response.ErrorFrom(c, err) {
  113. response.Success(c, out)
  114. }
  115. }
  116. func (h *IntelligentTestHandler) Records(c *gin.Context) {
  117. actor, ok := intelligentAdminActor(c)
  118. if !ok {
  119. return
  120. }
  121. f, ok := ParseIntelligentTestFilter(c)
  122. if !ok {
  123. return
  124. }
  125. out, err := h.svc.Records(c.Request.Context(), actor, f)
  126. if !response.ErrorFrom(c, err) {
  127. response.Success(c, out)
  128. }
  129. }
  130. func (h *IntelligentTestHandler) Get(c *gin.Context) {
  131. actor, ok := intelligentAdminActor(c)
  132. if !ok {
  133. return
  134. }
  135. id, ok := IntelligentTestParamID(c, "id")
  136. if !ok {
  137. return
  138. }
  139. out, err := h.svc.Get(c.Request.Context(), actor, id)
  140. if !response.ErrorFrom(c, err) {
  141. response.Success(c, out)
  142. }
  143. }
  144. func (h *IntelligentTestHandler) Image(c *gin.Context) {
  145. actor, ok := intelligentAdminActor(c)
  146. if !ok {
  147. return
  148. }
  149. id, ok := IntelligentTestParamID(c, "id")
  150. if !ok {
  151. return
  152. }
  153. out, err := h.svc.Get(c.Request.Context(), actor, id)
  154. if response.ErrorFrom(c, err) {
  155. return
  156. }
  157. WriteIntelligentTestImage(c, out.ResultImage)
  158. }
  159. func (h *IntelligentTestHandler) Run(c *gin.Context) {
  160. actor, ok := intelligentAdminActor(c)
  161. if !ok {
  162. return
  163. }
  164. c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10)
  165. var req service.IntelligentTestEnqueue
  166. if err := c.ShouldBindJSON(&req); err != nil {
  167. response.BadRequest(c, "invalid test request")
  168. return
  169. }
  170. out, err := h.svc.Enqueue(c.Request.Context(), actor, req)
  171. if !response.ErrorFrom(c, err) {
  172. response.Accepted(c, out)
  173. }
  174. }
  175. func (h *IntelligentTestHandler) Settings(c *gin.Context) {
  176. actor, ok := intelligentAdminActor(c)
  177. if !ok {
  178. return
  179. }
  180. out, err := h.svc.Settings(c.Request.Context(), actor)
  181. if !response.ErrorFrom(c, err) {
  182. response.Success(c, gin.H{"items": out})
  183. }
  184. }
  185. func (h *IntelligentTestHandler) UpdateSetting(c *gin.Context) {
  186. actor, ok := intelligentAdminActor(c)
  187. if !ok {
  188. return
  189. }
  190. c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 128<<10)
  191. var req service.IntelligentTestSetting
  192. if err := c.ShouldBindJSON(&req); err != nil {
  193. response.BadRequest(c, "invalid test settings")
  194. return
  195. }
  196. req.TestType = c.Param("test_type")
  197. if err := h.svc.UpdateSetting(c.Request.Context(), actor, &req); response.ErrorFrom(c, err) {
  198. return
  199. }
  200. response.Success(c, gin.H{"updated": true})
  201. }
  202. func (h *IntelligentTestHandler) Cancel(c *gin.Context) {
  203. actor, ok := intelligentAdminActor(c)
  204. if !ok {
  205. return
  206. }
  207. id, ok := IntelligentTestParamID(c, "id")
  208. if !ok {
  209. return
  210. }
  211. out, err := h.svc.Cancel(c.Request.Context(), actor, id)
  212. if !response.ErrorFrom(c, err) {
  213. response.Success(c, out)
  214. }
  215. }
  216. func (h *IntelligentTestHandler) Reevaluate(c *gin.Context) {
  217. actor, ok := intelligentAdminActor(c)
  218. if !ok {
  219. return
  220. }
  221. id, ok := IntelligentTestParamID(c, "id")
  222. if !ok {
  223. return
  224. }
  225. out, err := h.svc.Reevaluate(c.Request.Context(), actor, id)
  226. if !response.ErrorFrom(c, err) {
  227. response.Success(c, out)
  228. }
  229. }
  230. func (h *IntelligentTestHandler) PreviewEvaluation(c *gin.Context) {
  231. actor, ok := intelligentAdminActor(c)
  232. if !ok {
  233. return
  234. }
  235. c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 1<<20)
  236. var request struct {
  237. Output string `json:"output"`
  238. Config service.IntelligentTestConfig `json:"config"`
  239. }
  240. if err := c.ShouldBindJSON(&request); err != nil {
  241. response.BadRequest(c, "无效的规则试判请求")
  242. return
  243. }
  244. out, err := h.svc.PreviewEvaluation(c.Request.Context(), actor, request.Output, request.Config)
  245. if !response.ErrorFrom(c, err) {
  246. response.Success(c, out)
  247. }
  248. }
  249. func WriteIntelligentTestImage(c *gin.Context, raw string) {
  250. if raw == "" {
  251. response.NotFound(c, "test image not found")
  252. return
  253. }
  254. safe, err := service.SanitizeIntelligentTestSVG(raw)
  255. if err != nil {
  256. response.NotFound(c, "test image unavailable")
  257. return
  258. }
  259. c.Header("Content-Security-Policy", "default-src 'none'; style-src 'none'; sandbox")
  260. c.Header("X-Content-Type-Options", "nosniff")
  261. c.Header("Cache-Control", "no-store, private")
  262. c.Header("Content-Disposition", "inline; filename=test-result.svg")
  263. c.Data(http.StatusOK, "image/svg+xml; charset=utf-8", []byte(safe))
  264. }