Skip to content

Commit

Permalink
feat(notification): bye email if user reached daily email notificatio…
Browse files Browse the repository at this point in the history
…n threshold
  • Loading branch information
peterbitfly committed Oct 3, 2024
1 parent 7c610d2 commit 49b763d
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 25 deletions.
17 changes: 17 additions & 0 deletions backend/cmd/notification_sender/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package notification_sender

import (
"context"
"flag"
"fmt"
"net/http"
"os"
"sync"
"time"

"github.com/go-redis/redis/v8"
"github.com/gobitfly/beaconchain/pkg/commons/cache"
"github.com/gobitfly/beaconchain/pkg/commons/db"
"github.com/gobitfly/beaconchain/pkg/commons/log"
Expand Down Expand Up @@ -147,6 +149,21 @@ func Run() {
}()
}

// Initialize the persistent redis client
wg.Add(1)
go func() {
defer wg.Done()
rdc := redis.NewClient(&redis.Options{
Addr: cfg.RedisSessionStoreEndpoint,
ReadTimeout: time.Second * 60,
})

if err := rdc.Ping(context.Background()).Err(); err != nil {
log.Fatal(err, "error connecting to persistent redis store", 0)
}
db.PersistentRedisDbClient = rdc
}()

wg.Wait()

defer db.ReaderDb.Close()
Expand Down
29 changes: 13 additions & 16 deletions backend/pkg/commons/db/subscriptions.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package db

import (
"database/sql"
"context"
"encoding/hex"

"fmt"
Expand Down Expand Up @@ -278,22 +278,19 @@ func UpdateSubscriptionLastSent(tx *sqlx.Tx, ts uint64, epoch uint64, subID uint
return err
}

// CountSentMail increases the count of sent mails in the table `mails_sent` for this day.
func CountSentMail(email string) error {
// CountSentMail increases the count of sent mails for this day.
func CountSentMail(email string) (int64, error) {
day := time.Now().Truncate(utils.Day).Unix()
_, err := FrontendWriterDB.Exec(`
INSERT INTO mails_sent (email, ts, cnt) VALUES ($1, TO_TIMESTAMP($2), 1)
ON CONFLICT (email, ts) DO UPDATE SET cnt = mails_sent.cnt+1`, email, day)
return err
}
key := fmt.Sprintf("n_mails:%s:%d", email, day)

// GetMailsSentCount returns the number of sent mails for the day of the passed time.
func GetMailsSentCount(email string, t time.Time) (int, error) {
day := t.Truncate(utils.Day).Unix()
count := 0
err := FrontendWriterDB.Get(&count, "SELECT cnt FROM mails_sent WHERE email = $1 AND ts = TO_TIMESTAMP($2)", email, day)
if err == sql.ErrNoRows {
return 0, nil
pipe := PersistentRedisDbClient.TxPipeline()
incr := pipe.Incr(context.Background(), key)
pipe.Expire(context.Background(), key, utils.Day)
_, err := pipe.Exec(context.Background())

if incr.Err() != nil {
return 0, incr.Err()
}
return count, err

return incr.Val(), err
}
26 changes: 17 additions & 9 deletions backend/pkg/commons/mail/mail.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package mail
import (
"bytes"
"context"
"html/template"

"fmt"
"net/smtp"
Expand Down Expand Up @@ -74,23 +75,30 @@ func createTextMessage(msg types.Email) string {
func SendMailRateLimited(to, subject string, msg types.Email, attachment []types.EmailAttachment) error {
if utils.Config.Frontend.MaxMailsPerEmailPerDay > 0 {
now := time.Now()
count, err := db.GetMailsSentCount(to, now)
count, err := db.CountSentMail(to)
if err != nil {
return err
}
if count >= utils.Config.Frontend.MaxMailsPerEmailPerDay {
if count > int64(utils.Config.Frontend.MaxMailsPerEmailPerDay) {
timeLeft := now.Add(utils.Day).Truncate(utils.Day).Sub(now)
return &types.RateLimitError{TimeLeft: timeLeft}
} else if count == int64(utils.Config.Frontend.MaxMailsPerEmailPerDay) {
// send an email if this was the last email for today
err := SendHTMLMail(to,
"beaconcha.in - Email notification threshold limit reached",
types.Email{
Title: "Email notification threshold limit reached",
//nolint: gosec
Body: template.HTML(fmt.Sprintf("You have reached the email notification threshold limit of %d emails per day. Further notification emails will be suppressed until tomorrow.", utils.Config.Frontend.MaxMailsPerEmailPerDay)),
},
[]types.EmailAttachment{})
if err != nil {
return err
}
}
}

err := db.CountSentMail(to)
if err != nil {
// only log if counting did not work
return fmt.Errorf("error counting sent email: %v", err)
}

err = SendHTMLMail(to, subject, msg, attachment)
err := SendHTMLMail(to, subject, msg, attachment)
if err != nil {
return err
}
Expand Down

0 comments on commit 49b763d

Please sign in to comment.