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  }