ntfy.go 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. package notification
  2. import (
  3. "bytes"
  4. "context"
  5. "encoding/json"
  6. "fmt"
  7. "github.com/0xJacky/Nginx-UI/model"
  8. "github.com/uozi-tech/cosy/map2struct"
  9. "net/http"
  10. "strconv"
  11. )
  12. const (
  13. DEFAULT_NTFY_PRIORITY = 3
  14. DEFAULT_NTFY_ICON = "https://nginxui.com/assets/logo.svg"
  15. )
  16. // @external_notifier(Ntfy)
  17. type Ntfy struct {
  18. ServerURL string `json:"server_url" title:"Server URL"`
  19. Topic string `json:"topic" title:"Topic"`
  20. Priority string `json:"priority" title:"Priority"`
  21. Tags string `json:"tags" title:"Tags"`
  22. Click string `json:"click" title:"Click URL"`
  23. Actions string `json:"actions" title:"Actions"`
  24. Username string `json:"username" title:"Username"`
  25. Password string `json:"password" title:"Password"`
  26. Token string `json:"token" title:"Token"`
  27. }
  28. type NtfyMessage struct {
  29. Topic string `json:"topic,omitempty"`
  30. Message string `json:"message,omitempty"`
  31. Title string `json:"title,omitempty"`
  32. Priority int `json:"priority,omitempty"`
  33. Tags []string `json:"tags,omitempty"`
  34. Click string `json:"click,omitempty"`
  35. Actions []interface{} `json:"actions,omitempty"`
  36. Icon string `json:"icon,omitempty"`
  37. }
  38. func init() {
  39. RegisterExternalNotifier("ntfy", func(ctx context.Context, n *model.ExternalNotify, msg *ExternalMessage) error {
  40. ntfyConfig := &Ntfy{}
  41. err := map2struct.WeakDecode(n.Config, ntfyConfig)
  42. if err != nil {
  43. return err
  44. }
  45. if ntfyConfig.ServerURL == "" || ntfyConfig.Topic == "" {
  46. return ErrInvalidNotifierConfig
  47. }
  48. // Convert priority string to int
  49. priority := DEFAULT_NTFY_PRIORITY
  50. if ntfyConfig.Priority != "" {
  51. p, err := strconv.Atoi(ntfyConfig.Priority)
  52. if err != nil {
  53. return fmt.Errorf("invalid priority: %w", err)
  54. }
  55. if p < 1 || p > 5 {
  56. return fmt.Errorf("invalid priority: must be between 1 and 5")
  57. }
  58. priority = p
  59. }
  60. // Prepare the message
  61. ntfyMsg := NtfyMessage{
  62. Topic: ntfyConfig.Topic,
  63. Message: msg.GetContent(n.Language),
  64. Title: msg.GetTitle(n.Language),
  65. Priority: priority,
  66. Icon: DEFAULT_NTFY_ICON,
  67. Click: ntfyConfig.Click,
  68. }
  69. // Add tags if provided
  70. if ntfyConfig.Tags != "" {
  71. var tags []string
  72. if err := json.Unmarshal([]byte(ntfyConfig.Tags), &tags); err != nil {
  73. return fmt.Errorf("invalid tags: %w", err)
  74. }
  75. ntfyMsg.Tags = tags
  76. }
  77. // Add actions if provided
  78. if ntfyConfig.Actions != "" {
  79. var actions []interface{}
  80. if err := json.Unmarshal([]byte(ntfyConfig.Actions), &actions); err != nil {
  81. return fmt.Errorf("invalid actions: %w", err)
  82. }
  83. ntfyMsg.Actions = actions
  84. }
  85. // Create HTTP request
  86. jsonData, err := json.Marshal(ntfyMsg)
  87. if err != nil {
  88. return fmt.Errorf("failed to marshal ntfy message: %w", err)
  89. }
  90. req, err := http.NewRequestWithContext(ctx, "POST", ntfyConfig.ServerURL, bytes.NewBuffer(jsonData))
  91. if err != nil {
  92. return fmt.Errorf("failed to create HTTP request: %w", err)
  93. }
  94. // Set headers
  95. req.Header.Set("Content-Type", "application/json")
  96. req.Header.Set("User-Agent", "Nginx-UI")
  97. if ntfyConfig.Token != "" {
  98. req.Header.Set("Authorization", "Bearer "+ntfyConfig.Token)
  99. } else if ntfyConfig.Username != "" && ntfyConfig.Password != "" {
  100. req.SetBasicAuth(ntfyConfig.Username, ntfyConfig.Password)
  101. }
  102. // Send request
  103. client := &http.Client{}
  104. resp, err := client.Do(req)
  105. if err != nil {
  106. return fmt.Errorf("failed to send ntfy request: %w", err)
  107. }
  108. defer resp.Body.Close()
  109. // Check response status
  110. if resp.StatusCode < 200 || resp.StatusCode >= 300 {
  111. return fmt.Errorf("ntfy request failed with status: %d", resp.StatusCode)
  112. }
  113. return nil
  114. })
  115. }