eintopf.info@v0.13.16/service/notification/notification.go (about) 1 // Copyright (C) 2024 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package notification 17 18 import ( 19 "context" 20 "fmt" 21 "time" 22 23 "eintopf.info/internal/crud" 24 "eintopf.info/service/oqueue" 25 "eintopf.info/service/user" 26 ) 27 28 type Notification struct { 29 ID string `json:"id"` 30 CreatedAt time.Time `json:"createdAt" db:"created_at"` 31 UserID string `json:"userId" db:"user_id"` 32 Subject string `json:"subject"` 33 Message string `json:"message"` 34 LinkTitle string `json:"linkTitle" db:"link_title"` 35 LinkURL string `json:"linkUrl" db:"link_url"` 36 Viewed bool `json:"viewed"` 37 } 38 39 type Link struct { 40 Title string `json:"title"` 41 URL string `json:"url"` 42 } 43 44 func (n Notification) Identifier() string { 45 return n.ID 46 } 47 48 type NewNotification struct { 49 UserID string `json:"userId"` 50 Subject string `json:"subject"` 51 Message string `json:"message"` 52 LinkTitle string `json:"linkTitle"` 53 LinkURL string `json:"linkUrl"` 54 } 55 56 func NotifictaionFromNewNotification(n *NewNotification, id string) *Notification { 57 return &Notification{ 58 ID: id, 59 CreatedAt: time.Now(), 60 UserID: n.UserID, 61 Subject: n.Subject, 62 Message: n.Message, 63 LinkTitle: n.LinkTitle, 64 LinkURL: n.LinkURL, 65 Viewed: false, 66 } 67 } 68 69 // FindFilters defines for notifications. 70 type Filters struct { 71 ID *string `json:"id,omitempty" db:"id"` 72 UserID *string `json:"userId,omitempty" db:"user_id"` 73 Subject *string `json:"subject,omitempty" db:"subject"` 74 Message *string `json:"message,omitempty" db:"message"` 75 Viewed *bool `json:"viewed,omitempty" db:"viewed"` 76 } 77 78 type Storer = crud.Storer[NewNotification, Notification, Filters] 79 80 // TODO: ensure this is deleted, when the user gets deleted. 81 type Settings struct { 82 UserID string `json:"userId" db:"user_id"` 83 Email bool `json:"email" db:"email"` 84 } 85 86 func (n Settings) Identifier() string { 87 return n.UserID 88 } 89 90 func SettingsFromNewSettings(n *NewSettings, id string) *Settings { 91 return &Settings{ 92 UserID: n.UserID, 93 Email: n.Email, 94 } 95 } 96 97 type NewSettings struct { 98 UserID string `json:"userId" db:"user_id"` 99 Email bool `json:"email" db:"email"` 100 } 101 102 type SettingsFilters struct { 103 UserID *string `json:"userId,omitempty" db:"user_id"` 104 Email *bool `json:"email,omitempty" db:"email"` 105 } 106 107 type SettingsStorer = crud.Storer[NewSettings, Settings, SettingsFilters] 108 109 type Mailer interface { 110 Send(email, subject, body string) error 111 } 112 113 type Service struct { 114 mailer Mailer 115 store Storer 116 settingsStore SettingsStorer 117 user user.Storer 118 } 119 120 func NewService(mailer Mailer, store Storer, settingsStore SettingsStorer, user user.Storer) *Service { 121 return &Service{ 122 mailer: mailer, 123 store: store, 124 settingsStore: settingsStore, 125 user: user, 126 } 127 } 128 129 type Notifier interface { 130 Notify(ctx context.Context, userID string, subject string, message string, link Link) error 131 } 132 133 func (s *Service) ConsumeOperation(op oqueue.Operation) error { 134 switch op := op.(type) { 135 case user.DeleteOperation: 136 _ = s.settingsStore.Delete(context.Background(), op.ID) 137 } 138 return nil 139 } 140 141 func (s *Service) Notify(ctx context.Context, userID string, subject string, message string, link Link) error { 142 _, err := s.store.Create(ctx, &NewNotification{UserID: userID, Subject: subject, Message: message, LinkTitle: link.Title, LinkURL: link.URL}) 143 if err != nil { 144 return fmt.Errorf("create notification: %s", err) 145 } 146 settings, err := s.settingsStore.FindByID(ctx, userID) 147 if err != nil { 148 return fmt.Errorf("find settings: %s", err) 149 } 150 if settings == nil { 151 // Settings does not exist yet. 152 return nil 153 } 154 // TODO: enable email 155 // if settings.Email { 156 // user, err := s.user.FindByID(ctx, userID) 157 // if err != nil { 158 // return fmt.Errorf("find user: %s", err) 159 // } 160 // // TODO: use email template 161 // err = s.mailer.Send(user.Email, subject, message) 162 // if err != nil { 163 // return fmt.Errorf("send mail: %s", err) 164 // } 165 // } 166 return nil 167 } 168 169 func (s *Service) Store() Storer { 170 return s.store 171 } 172 173 func (s *Service) SettingsStore() SettingsStorer { 174 return s.settingsStore 175 }