security_policy_handler.go 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. package admin
  2. import (
  3. "errors"
  4. "strconv"
  5. "github.com/Wei-Shaw/sub2api/internal/handler/dto"
  6. "github.com/Wei-Shaw/sub2api/internal/pkg/response"
  7. "github.com/Wei-Shaw/sub2api/internal/service"
  8. "github.com/gin-gonic/gin"
  9. )
  10. // SecurityPolicyHandler 管理安全策略自定义词与会话解封。
  11. // 分组开关本身走分组管理接口(PUT /admin/groups/:id)。
  12. type SecurityPolicyHandler struct {
  13. service *service.SecurityPolicyService
  14. }
  15. func NewSecurityPolicyHandler(svc *service.SecurityPolicyService) *SecurityPolicyHandler {
  16. return &SecurityPolicyHandler{service: svc}
  17. }
  18. func (h *SecurityPolicyHandler) requireService(c *gin.Context) bool {
  19. if h == nil || h.service == nil {
  20. response.ErrorFrom(c, errors.New("security policy service unavailable"))
  21. return false
  22. }
  23. return true
  24. }
  25. // ListBuiltin 返回内置 seed 词包(只读,供管理端审计/调优参考)。
  26. func (h *SecurityPolicyHandler) ListBuiltin(c *gin.Context) {
  27. if !h.requireService(c) {
  28. return
  29. }
  30. seeds := h.service.GetBuiltinKeywords()
  31. out := make([]dto.SecurityPolicyKeywordSeed, 0, len(seeds))
  32. for _, seed := range seeds {
  33. out = append(out, dto.SecurityPolicyKeywordSeedFromService(seed))
  34. }
  35. response.Success(c, gin.H{"keywords": out, "count": len(out)})
  36. }
  37. // ListKeywords 查询自定义词:?group_id=(缺省全部,0 仅全局)&include_disabled=true。
  38. func (h *SecurityPolicyHandler) ListKeywords(c *gin.Context) {
  39. if !h.requireService(c) {
  40. return
  41. }
  42. var groupID *int64
  43. if raw := c.Query("group_id"); raw != "" {
  44. id, err := strconv.ParseInt(raw, 10, 64)
  45. if err != nil || id < 0 {
  46. response.BadRequest(c, "Invalid group_id")
  47. return
  48. }
  49. groupID = &id
  50. }
  51. includeDisabled := c.Query("include_disabled") == "true"
  52. words, err := h.service.ListKeywords(c.Request.Context(), groupID, includeDisabled)
  53. if err != nil {
  54. response.ErrorFrom(c, err)
  55. return
  56. }
  57. response.Success(c, gin.H{"keywords": dto.SecurityPolicyKeywordsFromService(words)})
  58. }
  59. type securityPolicyKeywordCreateRequest struct {
  60. GroupID *int64 `json:"group_id"`
  61. Keyword string `json:"keyword"`
  62. Category string `json:"category"`
  63. }
  64. // CreateKeyword 新建自定义词(group_id 缺省/0 为全局词)。
  65. func (h *SecurityPolicyHandler) CreateKeyword(c *gin.Context) {
  66. if !h.requireService(c) {
  67. return
  68. }
  69. var req securityPolicyKeywordCreateRequest
  70. if err := c.ShouldBindJSON(&req); err != nil {
  71. response.BadRequest(c, "Invalid request: "+err.Error())
  72. return
  73. }
  74. if req.GroupID != nil && *req.GroupID <= 0 {
  75. req.GroupID = nil
  76. }
  77. word, err := h.service.CreateKeyword(c.Request.Context(), service.SecurityPolicyKeywordInput{
  78. GroupID: req.GroupID,
  79. Keyword: req.Keyword,
  80. Category: req.Category,
  81. })
  82. if err != nil {
  83. response.ErrorFrom(c, err)
  84. return
  85. }
  86. response.Success(c, dto.SecurityPolicyKeywordFromService(word))
  87. }
  88. type securityPolicyKeywordUpdateRequest struct {
  89. Keyword *string `json:"keyword"`
  90. Category *string `json:"category"`
  91. Enabled *bool `json:"enabled"`
  92. }
  93. // UpdateKeyword 更新自定义词(词/分类/启用)。
  94. func (h *SecurityPolicyHandler) UpdateKeyword(c *gin.Context) {
  95. if !h.requireService(c) {
  96. return
  97. }
  98. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  99. if err != nil || id <= 0 {
  100. response.BadRequest(c, "Invalid keyword ID")
  101. return
  102. }
  103. var req securityPolicyKeywordUpdateRequest
  104. if err := c.ShouldBindJSON(&req); err != nil {
  105. response.BadRequest(c, "Invalid request: "+err.Error())
  106. return
  107. }
  108. word, err := h.service.UpdateKeyword(c.Request.Context(), id, service.SecurityPolicyKeywordUpdate{
  109. Keyword: req.Keyword,
  110. Category: req.Category,
  111. Enabled: req.Enabled,
  112. })
  113. if err != nil {
  114. response.ErrorFrom(c, err)
  115. return
  116. }
  117. response.Success(c, dto.SecurityPolicyKeywordFromService(word))
  118. }
  119. // DeleteKeyword 软删除自定义词。
  120. func (h *SecurityPolicyHandler) DeleteKeyword(c *gin.Context) {
  121. if !h.requireService(c) {
  122. return
  123. }
  124. id, err := strconv.ParseInt(c.Param("id"), 10, 64)
  125. if err != nil || id <= 0 {
  126. response.BadRequest(c, "Invalid keyword ID")
  127. return
  128. }
  129. if err := h.service.DeleteKeyword(c.Request.Context(), id); err != nil {
  130. response.ErrorFrom(c, err)
  131. return
  132. }
  133. response.Success(c, gin.H{"message": "Keyword deleted successfully"})
  134. }
  135. type securityPolicyUnblockRequest struct {
  136. GroupID int64 `json:"group_id"`
  137. APIKeyID int64 `json:"api_key_id"`
  138. }
  139. // UnblockSession 清除指定分组+Key 的全部会话封禁(需新建会话的反向操作)。
  140. func (h *SecurityPolicyHandler) UnblockSession(c *gin.Context) {
  141. if !h.requireService(c) {
  142. return
  143. }
  144. var req securityPolicyUnblockRequest
  145. if err := c.ShouldBindJSON(&req); err != nil {
  146. response.BadRequest(c, "Invalid request: "+err.Error())
  147. return
  148. }
  149. if req.GroupID <= 0 || req.APIKeyID <= 0 {
  150. response.BadRequest(c, "group_id and api_key_id must be positive")
  151. return
  152. }
  153. unblocked, err := h.service.UnblockSessionScope(c.Request.Context(), req.GroupID, req.APIKeyID)
  154. if err != nil {
  155. response.ErrorFrom(c, err)
  156. return
  157. }
  158. response.Success(c, gin.H{"unblocked": unblocked})
  159. }