notify_email_entry.go 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. package service
  2. import (
  3. "encoding/json"
  4. "strings"
  5. )
  6. // NotifyEmailEntry represents a notification email with enable/disable and verification state.
  7. // All emails are user-managed; maximum 3 entries per user.
  8. type NotifyEmailEntry struct {
  9. Email string `json:"email"`
  10. Disabled bool `json:"disabled"`
  11. Verified bool `json:"verified"`
  12. }
  13. // parseNotifyEmails parses a JSON string into []NotifyEmailEntry.
  14. // It auto-detects the format:
  15. // - Old format ["email1","email2"] → converted to [{email, disabled:false, verified:true}, ...]
  16. // - New format [{email,disabled,verified}, ...] → parsed directly
  17. //
  18. // Returns nil on empty/invalid input.
  19. func ParseNotifyEmails(raw string) []NotifyEmailEntry {
  20. raw = strings.TrimSpace(raw)
  21. if raw == "" || raw == "[]" {
  22. return nil
  23. }
  24. // Try parsing as new format first (array of objects)
  25. var entries []NotifyEmailEntry
  26. if err := json.Unmarshal([]byte(raw), &entries); err == nil && len(entries) > 0 {
  27. // Verify it's actually the new format by checking the first element
  28. // json.Unmarshal into []NotifyEmailEntry succeeds even for ["string"]
  29. // because it tries to fit "string" into NotifyEmailEntry and gets zero values.
  30. // We need to detect old format explicitly.
  31. if !isOldStringArrayFormat(raw) {
  32. return entries
  33. }
  34. }
  35. // Try parsing as old format (array of strings)
  36. var emails []string
  37. if err := json.Unmarshal([]byte(raw), &emails); err == nil {
  38. result := make([]NotifyEmailEntry, 0, len(emails))
  39. for _, e := range emails {
  40. e = strings.TrimSpace(e)
  41. if e != "" {
  42. result = append(result, NotifyEmailEntry{
  43. Email: e,
  44. Disabled: false,
  45. Verified: false, // Old format emails default to unverified
  46. })
  47. }
  48. }
  49. return result
  50. }
  51. return nil
  52. }
  53. // isOldStringArrayFormat checks if the JSON is a string array like ["email1","email2"].
  54. func isOldStringArrayFormat(raw string) bool {
  55. var arr []json.RawMessage
  56. if err := json.Unmarshal([]byte(raw), &arr); err != nil || len(arr) == 0 {
  57. return false
  58. }
  59. // Check if first element starts with a quote (string) vs { (object)
  60. first := strings.TrimSpace(string(arr[0]))
  61. return len(first) > 0 && first[0] == '"'
  62. }
  63. // marshalNotifyEmails serializes []NotifyEmailEntry to JSON string.
  64. func MarshalNotifyEmails(entries []NotifyEmailEntry) string {
  65. if len(entries) == 0 {
  66. return "[]"
  67. }
  68. data, err := json.Marshal(entries)
  69. if err != nil {
  70. return "[]"
  71. }
  72. return string(data)
  73. }