| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- package admin
- import (
- "github.com/Wei-Shaw/sub2api/internal/pkg/response"
- "github.com/Wei-Shaw/sub2api/internal/server/middleware"
- "github.com/Wei-Shaw/sub2api/internal/service"
- "github.com/gin-gonic/gin"
- "net/http"
- "strconv"
- "time"
- )
- type IntelligentTestHandler struct {
- svc *service.IntelligentTestService
- }
- func NewIntelligentTestHandler(svc *service.IntelligentTestService) *IntelligentTestHandler {
- return &IntelligentTestHandler{svc: svc}
- }
- func intelligentAdminActor(c *gin.Context) (int64, bool) {
- subject, ok := middleware.GetAuthSubjectFromContext(c)
- if !ok || subject.UserID <= 0 {
- response.Unauthorized(c, "authentication required")
- return 0, false
- }
- role, ok := middleware.GetUserRoleFromContext(c)
- if !ok || (role != "admin" && role != "super_admin") {
- response.Forbidden(c, "administrator access required")
- return 0, false
- }
- return subject.UserID, true
- }
- func ParseIntelligentTestFilter(c *gin.Context) (service.IntelligentTestFilter, bool) {
- 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")}
- if f.AccountType == "" {
- f.AccountType = c.Query("type")
- }
- if len(f.Search) > 200 {
- response.BadRequest(c, "search is too long")
- return f, false
- }
- for key, dst := range map[string]*int{"page": &f.Page, "page_size": &f.PageSize} {
- if raw := c.Query(key); raw != "" {
- v, err := strconv.Atoi(raw)
- if err != nil || v < 1 || v > 1000000 {
- response.BadRequest(c, "invalid "+key)
- return f, false
- }
- *dst = v
- }
- }
- for key, dst := range map[string]*int64{"account_id": &f.AccountID, "group_id": &f.GroupID} {
- if raw := c.Query(key); raw != "" {
- v, err := strconv.ParseInt(raw, 10, 64)
- if err != nil || v < 1 {
- response.BadRequest(c, "invalid "+key)
- return f, false
- }
- *dst = v
- }
- }
- if raw := c.Query("anti_degradation"); raw != "" {
- v, err := strconv.ParseBool(raw)
- if err != nil {
- response.BadRequest(c, "invalid anti_degradation")
- return f, false
- }
- f.AntiDegradation = &v
- }
- if raw := c.Query("only_abnormal"); raw != "" {
- v, err := strconv.ParseBool(raw)
- if err != nil {
- response.BadRequest(c, "invalid only_abnormal")
- return f, false
- }
- f.OnlyAbnormal = v
- }
- for key, dst := range map[string]**time.Time{"from": &f.From, "to": &f.To} {
- if raw := c.Query(key); raw != "" {
- v, err := time.Parse(time.RFC3339, raw)
- if err != nil {
- response.BadRequest(c, "invalid "+key+"; use RFC3339")
- return f, false
- }
- *dst = &v
- }
- }
- if f.From != nil && f.To != nil && f.From.After(*f.To) {
- response.BadRequest(c, "from must be before to")
- return f, false
- }
- if f.PageSize > 100 {
- f.PageSize = 100
- }
- return f, true
- }
- func IntelligentTestParamID(c *gin.Context, param string) (int64, bool) {
- id, err := strconv.ParseInt(c.Param(param), 10, 64)
- if err != nil || id < 1 {
- response.BadRequest(c, "invalid identifier")
- return 0, false
- }
- return id, true
- }
- func (h *IntelligentTestHandler) Accounts(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- f, ok := ParseIntelligentTestFilter(c)
- if !ok {
- return
- }
- out, err := h.svc.Accounts(c.Request.Context(), actor, f)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func (h *IntelligentTestHandler) Records(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- f, ok := ParseIntelligentTestFilter(c)
- if !ok {
- return
- }
- out, err := h.svc.Records(c.Request.Context(), actor, f)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func (h *IntelligentTestHandler) Get(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- id, ok := IntelligentTestParamID(c, "id")
- if !ok {
- return
- }
- out, err := h.svc.Get(c.Request.Context(), actor, id)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func (h *IntelligentTestHandler) Image(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- id, ok := IntelligentTestParamID(c, "id")
- if !ok {
- return
- }
- out, err := h.svc.Get(c.Request.Context(), actor, id)
- if response.ErrorFrom(c, err) {
- return
- }
- WriteIntelligentTestImage(c, out.ResultImage)
- }
- func (h *IntelligentTestHandler) Run(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 64<<10)
- var req service.IntelligentTestEnqueue
- if err := c.ShouldBindJSON(&req); err != nil {
- response.BadRequest(c, "invalid test request")
- return
- }
- out, err := h.svc.Enqueue(c.Request.Context(), actor, req)
- if !response.ErrorFrom(c, err) {
- response.Accepted(c, out)
- }
- }
- func (h *IntelligentTestHandler) Settings(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- out, err := h.svc.Settings(c.Request.Context(), actor)
- if !response.ErrorFrom(c, err) {
- response.Success(c, gin.H{"items": out})
- }
- }
- func (h *IntelligentTestHandler) UpdateSetting(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 128<<10)
- var req service.IntelligentTestSetting
- if err := c.ShouldBindJSON(&req); err != nil {
- response.BadRequest(c, "invalid test settings")
- return
- }
- req.TestType = c.Param("test_type")
- if err := h.svc.UpdateSetting(c.Request.Context(), actor, &req); response.ErrorFrom(c, err) {
- return
- }
- response.Success(c, gin.H{"updated": true})
- }
- func (h *IntelligentTestHandler) Cancel(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- id, ok := IntelligentTestParamID(c, "id")
- if !ok {
- return
- }
- out, err := h.svc.Cancel(c.Request.Context(), actor, id)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func (h *IntelligentTestHandler) Reevaluate(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- id, ok := IntelligentTestParamID(c, "id")
- if !ok {
- return
- }
- out, err := h.svc.Reevaluate(c.Request.Context(), actor, id)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func (h *IntelligentTestHandler) PreviewEvaluation(c *gin.Context) {
- actor, ok := intelligentAdminActor(c)
- if !ok {
- return
- }
- c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 1<<20)
- var request struct {
- Output string `json:"output"`
- Config service.IntelligentTestConfig `json:"config"`
- }
- if err := c.ShouldBindJSON(&request); err != nil {
- response.BadRequest(c, "无效的规则试判请求")
- return
- }
- out, err := h.svc.PreviewEvaluation(c.Request.Context(), actor, request.Output, request.Config)
- if !response.ErrorFrom(c, err) {
- response.Success(c, out)
- }
- }
- func WriteIntelligentTestImage(c *gin.Context, raw string) {
- if raw == "" {
- response.NotFound(c, "test image not found")
- return
- }
- safe, err := service.SanitizeIntelligentTestSVG(raw)
- if err != nil {
- response.NotFound(c, "test image unavailable")
- return
- }
- c.Header("Content-Security-Policy", "default-src 'none'; style-src 'none'; sandbox")
- c.Header("X-Content-Type-Options", "nosniff")
- c.Header("Cache-Control", "no-store, private")
- c.Header("Content-Disposition", "inline; filename=test-result.svg")
- c.Data(http.StatusOK, "image/svg+xml; charset=utf-8", []byte(safe))
- }
|