+27
-10
email/send.go
+27
-10
email/send.go
···
30
30
}
31
31
}
32
32
33
-
func (m *Mailer) Send(to, subject, htmlBody, textBody, unsubToken string) error {
33
+
func (m *Mailer) Send(to, subject, htmlBody, textBody, unsubToken, dashboardURL string) error {
34
34
addr := net.JoinHostPort(m.cfg.Host, fmt.Sprintf("%d", m.cfg.Port))
35
35
36
36
boundary := "==herald-boundary-a1b2c3d4e5f6=="
37
37
38
-
// Add unsubscribe footer if token provided
39
-
if unsubToken != "" {
40
-
unsubURL := m.unsubBaseURL + "/unsubscribe/" + unsubToken
41
-
42
-
htmlFooter := fmt.Sprintf(`<hr><p style="font-size: 12px; color: #666;"><a href="%s">Unsubscribe</a></p>`, unsubURL)
43
-
htmlBody = htmlBody + htmlFooter
44
-
45
-
textFooter := fmt.Sprintf("\n\n---\nUnsubscribe: %s\n", unsubURL)
46
-
textBody = textBody + textFooter
38
+
// Add footer with unsubscribe and dashboard links
39
+
var htmlFooter strings.Builder
40
+
var textFooter strings.Builder
41
+
42
+
if unsubToken != "" || dashboardURL != "" {
43
+
htmlFooter.WriteString(`<hr><p style="font-size: 12px; color: #666;">`)
44
+
textFooter.WriteString("\n\n---\n")
45
+
46
+
if dashboardURL != "" {
47
+
htmlFooter.WriteString(fmt.Sprintf(`<a href="%s">profile</a>`, dashboardURL))
48
+
textFooter.WriteString(fmt.Sprintf("profile: %s\n", dashboardURL))
49
+
}
50
+
51
+
if unsubToken != "" {
52
+
unsubURL := m.unsubBaseURL + "/unsubscribe/" + unsubToken
53
+
if dashboardURL != "" {
54
+
htmlFooter.WriteString(" • ")
55
+
textFooter.WriteString("")
56
+
}
57
+
htmlFooter.WriteString(fmt.Sprintf(`<a href="%s">Unsubscribe</a>`, unsubURL))
58
+
textFooter.WriteString(fmt.Sprintf("unsubscribe: %s\n", unsubURL))
59
+
}
60
+
61
+
htmlFooter.WriteString("</p>")
62
+
htmlBody = htmlBody + htmlFooter.String()
63
+
textBody = textBody + textFooter.String()
47
64
}
48
65
49
66
headers := make(map[string]string)
+1
-1
main.go
+1
-1
main.go
···
151
151
From: cfg.SMTP.From,
152
152
}, cfg.Origin)
153
153
154
-
sched := scheduler.NewScheduler(db, mailer, logger, 60*time.Second)
154
+
sched := scheduler.NewScheduler(db, mailer, logger, 60*time.Second, cfg.Origin)
155
155
156
156
sshServer := ssh.NewServer(ssh.Config{
157
157
Host: cfg.Host,
+31
-11
scheduler/scheduler.go
+31
-11
scheduler/scheduler.go
···
12
12
)
13
13
14
14
type Scheduler struct {
15
-
store *store.DB
16
-
mailer *email.Mailer
17
-
logger *log.Logger
18
-
interval time.Duration
15
+
store *store.DB
16
+
mailer *email.Mailer
17
+
logger *log.Logger
18
+
interval time.Duration
19
+
originURL string
19
20
}
20
21
21
-
func NewScheduler(st *store.DB, mailer *email.Mailer, logger *log.Logger, interval time.Duration) *Scheduler {
22
+
func NewScheduler(st *store.DB, mailer *email.Mailer, logger *log.Logger, interval time.Duration, originURL string) *Scheduler {
22
23
return &Scheduler{
23
-
store: st,
24
-
mailer: mailer,
25
-
logger: logger,
26
-
interval: interval,
24
+
store: st,
25
+
mailer: mailer,
26
+
logger: logger,
27
+
interval: interval,
28
+
originURL: originURL,
27
29
}
28
30
}
29
31
···
158
160
unsubToken = ""
159
161
}
160
162
163
+
// Get user fingerprint for dashboard URL
164
+
user, err := s.store.GetUserByID(ctx, cfg.UserID)
165
+
dashboardURL := ""
166
+
if err == nil {
167
+
dashboardURL = s.originURL + "/" + user.PubkeyFP
168
+
} else {
169
+
s.logger.Warn("failed to get user for dashboard URL", "err", err)
170
+
}
171
+
161
172
subject := "feed digest"
162
-
if err := s.mailer.Send(cfg.Email, subject, htmlBody, textBody, unsubToken); err != nil {
173
+
if err := s.mailer.Send(cfg.Email, subject, htmlBody, textBody, unsubToken, dashboardURL); err != nil {
163
174
return 0, fmt.Errorf("send email: %w", err)
164
175
}
165
176
···
286
297
unsubToken = ""
287
298
}
288
299
300
+
// Get user fingerprint for dashboard URL
301
+
user, err := s.store.GetUserByID(ctx, cfg.UserID)
302
+
dashboardURL := ""
303
+
if err == nil {
304
+
dashboardURL = s.originURL + "/" + user.PubkeyFP
305
+
} else {
306
+
s.logger.Warn("failed to get user for dashboard URL", "err", err)
307
+
}
308
+
289
309
subject := "feed digest"
290
-
if err := s.mailer.Send(cfg.Email, subject, htmlBody, textBody, unsubToken); err != nil {
310
+
if err := s.mailer.Send(cfg.Email, subject, htmlBody, textBody, unsubToken, dashboardURL); err != nil {
291
311
return fmt.Errorf("send email: %w", err)
292
312
}
293
313