ops_alerts.go 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232
  1. package service
  2. import (
  3. "context"
  4. "database/sql"
  5. "errors"
  6. "strings"
  7. "time"
  8. infraerrors "github.com/Wei-Shaw/sub2api/internal/pkg/errors"
  9. )
  10. func (s *OpsService) ListAlertRules(ctx context.Context) ([]*OpsAlertRule, error) {
  11. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  12. return nil, err
  13. }
  14. if s.opsRepo == nil {
  15. return []*OpsAlertRule{}, nil
  16. }
  17. return s.opsRepo.ListAlertRules(ctx)
  18. }
  19. func (s *OpsService) CreateAlertRule(ctx context.Context, rule *OpsAlertRule) (*OpsAlertRule, error) {
  20. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  21. return nil, err
  22. }
  23. if s.opsRepo == nil {
  24. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  25. }
  26. if rule == nil {
  27. return nil, infraerrors.BadRequest("INVALID_RULE", "invalid rule")
  28. }
  29. created, err := s.opsRepo.CreateAlertRule(ctx, rule)
  30. if err != nil {
  31. return nil, err
  32. }
  33. return created, nil
  34. }
  35. func (s *OpsService) UpdateAlertRule(ctx context.Context, rule *OpsAlertRule) (*OpsAlertRule, error) {
  36. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  37. return nil, err
  38. }
  39. if s.opsRepo == nil {
  40. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  41. }
  42. if rule == nil || rule.ID <= 0 {
  43. return nil, infraerrors.BadRequest("INVALID_RULE", "invalid rule")
  44. }
  45. updated, err := s.opsRepo.UpdateAlertRule(ctx, rule)
  46. if err != nil {
  47. if errors.Is(err, sql.ErrNoRows) {
  48. return nil, infraerrors.NotFound("OPS_ALERT_RULE_NOT_FOUND", "alert rule not found")
  49. }
  50. return nil, err
  51. }
  52. return updated, nil
  53. }
  54. func (s *OpsService) DeleteAlertRule(ctx context.Context, id int64) error {
  55. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  56. return err
  57. }
  58. if s.opsRepo == nil {
  59. return infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  60. }
  61. if id <= 0 {
  62. return infraerrors.BadRequest("INVALID_RULE_ID", "invalid rule id")
  63. }
  64. if err := s.opsRepo.DeleteAlertRule(ctx, id); err != nil {
  65. if errors.Is(err, sql.ErrNoRows) {
  66. return infraerrors.NotFound("OPS_ALERT_RULE_NOT_FOUND", "alert rule not found")
  67. }
  68. return err
  69. }
  70. return nil
  71. }
  72. func (s *OpsService) ListAlertEvents(ctx context.Context, filter *OpsAlertEventFilter) ([]*OpsAlertEvent, error) {
  73. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  74. return nil, err
  75. }
  76. if s.opsRepo == nil {
  77. return []*OpsAlertEvent{}, nil
  78. }
  79. return s.opsRepo.ListAlertEvents(ctx, filter)
  80. }
  81. func (s *OpsService) GetAlertEventByID(ctx context.Context, eventID int64) (*OpsAlertEvent, error) {
  82. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  83. return nil, err
  84. }
  85. if s.opsRepo == nil {
  86. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  87. }
  88. if eventID <= 0 {
  89. return nil, infraerrors.BadRequest("INVALID_EVENT_ID", "invalid event id")
  90. }
  91. ev, err := s.opsRepo.GetAlertEventByID(ctx, eventID)
  92. if err != nil {
  93. if errors.Is(err, sql.ErrNoRows) {
  94. return nil, infraerrors.NotFound("OPS_ALERT_EVENT_NOT_FOUND", "alert event not found")
  95. }
  96. return nil, err
  97. }
  98. if ev == nil {
  99. return nil, infraerrors.NotFound("OPS_ALERT_EVENT_NOT_FOUND", "alert event not found")
  100. }
  101. return ev, nil
  102. }
  103. func (s *OpsService) GetActiveAlertEvent(ctx context.Context, ruleID int64) (*OpsAlertEvent, error) {
  104. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  105. return nil, err
  106. }
  107. if s.opsRepo == nil {
  108. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  109. }
  110. if ruleID <= 0 {
  111. return nil, infraerrors.BadRequest("INVALID_RULE_ID", "invalid rule id")
  112. }
  113. return s.opsRepo.GetActiveAlertEvent(ctx, ruleID)
  114. }
  115. func (s *OpsService) CreateAlertSilence(ctx context.Context, input *OpsAlertSilence) (*OpsAlertSilence, error) {
  116. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  117. return nil, err
  118. }
  119. if s.opsRepo == nil {
  120. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  121. }
  122. if input == nil {
  123. return nil, infraerrors.BadRequest("INVALID_SILENCE", "invalid silence")
  124. }
  125. if input.RuleID <= 0 {
  126. return nil, infraerrors.BadRequest("INVALID_RULE_ID", "invalid rule id")
  127. }
  128. if strings.TrimSpace(input.Platform) == "" {
  129. return nil, infraerrors.BadRequest("INVALID_PLATFORM", "invalid platform")
  130. }
  131. if input.Until.IsZero() {
  132. return nil, infraerrors.BadRequest("INVALID_UNTIL", "invalid until")
  133. }
  134. created, err := s.opsRepo.CreateAlertSilence(ctx, input)
  135. if err != nil {
  136. return nil, err
  137. }
  138. return created, nil
  139. }
  140. func (s *OpsService) IsAlertSilenced(ctx context.Context, ruleID int64, platform string, groupID *int64, region *string, now time.Time) (bool, error) {
  141. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  142. return false, err
  143. }
  144. if s.opsRepo == nil {
  145. return false, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  146. }
  147. if ruleID <= 0 {
  148. return false, infraerrors.BadRequest("INVALID_RULE_ID", "invalid rule id")
  149. }
  150. if strings.TrimSpace(platform) == "" {
  151. return false, nil
  152. }
  153. return s.opsRepo.IsAlertSilenced(ctx, ruleID, platform, groupID, region, now)
  154. }
  155. func (s *OpsService) GetLatestAlertEvent(ctx context.Context, ruleID int64) (*OpsAlertEvent, error) {
  156. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  157. return nil, err
  158. }
  159. if s.opsRepo == nil {
  160. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  161. }
  162. if ruleID <= 0 {
  163. return nil, infraerrors.BadRequest("INVALID_RULE_ID", "invalid rule id")
  164. }
  165. return s.opsRepo.GetLatestAlertEvent(ctx, ruleID)
  166. }
  167. func (s *OpsService) CreateAlertEvent(ctx context.Context, event *OpsAlertEvent) (*OpsAlertEvent, error) {
  168. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  169. return nil, err
  170. }
  171. if s.opsRepo == nil {
  172. return nil, infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  173. }
  174. if event == nil {
  175. return nil, infraerrors.BadRequest("INVALID_EVENT", "invalid event")
  176. }
  177. created, err := s.opsRepo.CreateAlertEvent(ctx, event)
  178. if err != nil {
  179. return nil, err
  180. }
  181. return created, nil
  182. }
  183. func (s *OpsService) UpdateAlertEventStatus(ctx context.Context, eventID int64, status string, resolvedAt *time.Time) error {
  184. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  185. return err
  186. }
  187. if s.opsRepo == nil {
  188. return infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  189. }
  190. if eventID <= 0 {
  191. return infraerrors.BadRequest("INVALID_EVENT_ID", "invalid event id")
  192. }
  193. status = strings.TrimSpace(status)
  194. if status == "" {
  195. return infraerrors.BadRequest("INVALID_STATUS", "invalid status")
  196. }
  197. if status != OpsAlertStatusResolved && status != OpsAlertStatusManualResolved {
  198. return infraerrors.BadRequest("INVALID_STATUS", "invalid status")
  199. }
  200. return s.opsRepo.UpdateAlertEventStatus(ctx, eventID, status, resolvedAt)
  201. }
  202. func (s *OpsService) UpdateAlertEventEmailSent(ctx context.Context, eventID int64, emailSent bool) error {
  203. if err := s.RequireMonitoringEnabled(ctx); err != nil {
  204. return err
  205. }
  206. if s.opsRepo == nil {
  207. return infraerrors.ServiceUnavailable("OPS_REPO_UNAVAILABLE", "Ops repository not available")
  208. }
  209. if eventID <= 0 {
  210. return infraerrors.BadRequest("INVALID_EVENT_ID", "invalid event id")
  211. }
  212. return s.opsRepo.UpdateAlertEventEmailSent(ctx, eventID, emailSent)
  213. }