Skip to content

Commit 6201a93

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
fix panic
1 parent 50278ed commit 6201a93

File tree

2 files changed

+51
-14
lines changed

2 files changed

+51
-14
lines changed

email/templates.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ func (s *Sender) formatNotificationBody(sub *notifier.Subscription, thread *noti
1717
b.WriteString("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n")
1818
b.WriteString("<style>\n")
1919
b.WriteString("body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; line-height: 1.6; color: #333; max-width: 800px; margin: 0 auto; padding: 20px; background: #fff; }\n")
20-
b.WriteString(".post { margin-bottom: 30px; padding-bottom: 30px; border-bottom: 2px solid #e67e22; }\n")
21-
b.WriteString(".post:last-of-type { border-bottom: none; padding-bottom: 0; margin-bottom: 0; }\n")
20+
b.WriteString(".post { padding: 24px 0; border-bottom: 2px solid #e67e22; }\n")
21+
b.WriteString(".post:last-of-type { border-bottom: none; padding-bottom: 0; }\n")
2222
b.WriteString(".post:first-of-type { padding-top: 0; }\n")
2323
b.WriteString(".meta { margin-bottom: 12px; }\n")
2424
b.WriteString(".post-number { color: #7f8c8d; font-weight: 500; font-size: 1.1em; text-decoration: none; }\n")
@@ -28,7 +28,7 @@ func (s *Sender) formatNotificationBody(sub *notifier.Subscription, thread *noti
2828
b.WriteString(".content { margin: 15px 0; }\n")
2929
b.WriteString(".content img { max-width: 100%; height: auto; margin: 10px 0; display: block; }\n")
3030
b.WriteString(".content blockquote { border-left: 3px solid #ddd; padding-left: 15px; margin: 10px 0; color: #666; font-size: 0.95em; }\n")
31-
b.WriteString(".footer { margin-top: 30px; padding-top: 15px; font-size: 0.9em; color: #7f8c8d; }\n")
31+
b.WriteString(".footer { margin-top: 24px; padding-top: 12px; font-size: 0.9em; color: #7f8c8d; }\n")
3232
b.WriteString(".footer.with-border { border-top: 1px solid #ddd; }\n")
3333
b.WriteString(".footer a { color: #7f8c8d; text-decoration: underline; margin: 0 8px; }\n")
3434
b.WriteString(".footer a:first-child { margin-left: 0; }\n")
@@ -90,10 +90,10 @@ func (s *Sender) formatNotificationBody(sub *notifier.Subscription, thread *noti
9090
if len(posts) > 0 && posts[len(posts)-1].URL != "" {
9191
threadLink = posts[len(posts)-1].URL
9292
}
93-
b.WriteString(fmt.Sprintf("<a href=\"%s\">View thread</a>\n", escapeHTML(threadLink)))
93+
b.WriteString(fmt.Sprintf("<a href=\"%s\">View thread on ADVrider</a>\n", escapeHTML(threadLink)))
9494

9595
manageURL := fmt.Sprintf("%s/manage?token=%s", s.baseURL, url.QueryEscape(sub.Token))
96-
b.WriteString(fmt.Sprintf("<a href=\"%s\">Manage</a>\n", escapeHTML(manageURL)))
96+
b.WriteString(fmt.Sprintf("<a href=\"%s\">Manage subscriptions</a>\n", escapeHTML(manageURL)))
9797
b.WriteString("</div>\n")
9898

9999
b.WriteString("</body>\n</html>")
@@ -144,9 +144,9 @@ func (s *Sender) formatWelcomeBody(sub *notifier.Subscription, thread *notifier.
144144
b.WriteString("</div>\n")
145145

146146
b.WriteString("<div class=\"footer\">\n")
147-
b.WriteString(fmt.Sprintf("<a href=\"%s\">View thread</a>\n", escapeHTML(thread.ThreadURL)))
147+
b.WriteString(fmt.Sprintf("<a href=\"%s\">View thread on ADVrider</a>\n", escapeHTML(thread.ThreadURL)))
148148
b.WriteString(" &bull; \n")
149-
b.WriteString(fmt.Sprintf("<a href=\"%s\">Manage</a>\n", escapeHTML(manageURL)))
149+
b.WriteString(fmt.Sprintf("<a href=\"%s\">Manage subscriptions</a>\n", escapeHTML(manageURL)))
150150
b.WriteString("</div>\n")
151151

152152
b.WriteString("</body>\n</html>")

poll/poll.go

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,14 @@ func (m *Monitor) checkThreadForSubscribers(
259259

260260
for email, sub := range info.subscribers {
261261
thread := sub.Threads[info.threadID]
262+
if thread == nil {
263+
m.logger.Error("CRITICAL: Thread not found in subscriber's thread map - data corruption or logic error",
264+
"cycle", m.cycleNumber,
265+
"email", email,
266+
"thread_id", info.threadID,
267+
"thread_url", threadURL)
268+
continue
269+
}
262270

263271
m.logger.Info("Processing subscriber",
264272
"cycle", m.cycleNumber,
@@ -272,7 +280,7 @@ func (m *Monitor) checkThreadForSubscribers(
272280

273281
// First check for this subscriber - just record the latest post ID
274282
if thread.LastPostID == "" {
275-
if m.saveInitialState(ctx, sub, email, threadURL, latestPost.ID, savedEmails) {
283+
if m.saveInitialState(ctx, sub, email, info.threadID, threadURL, latestPost.ID, savedEmails) {
276284
hasUpdates = true
277285
}
278286
continue
@@ -286,7 +294,7 @@ func (m *Monitor) checkThreadForSubscribers(
286294
hasUpdates = true
287295
}
288296
} else {
289-
m.saveStateNoNewPosts(ctx, sub, email, threadURL, savedEmails)
297+
m.saveStateNoNewPosts(ctx, sub, email, info.threadID, threadURL, savedEmails)
290298
}
291299
}
292300

@@ -326,6 +334,13 @@ func (m *Monitor) fetchThreadPosts(
326334
// Update thread title for all subscribers if not set
327335
for _, sub := range info.subscribers {
328336
thread := sub.Threads[info.threadID]
337+
if thread == nil {
338+
m.logger.Error("CRITICAL: Thread not found when updating title - data corruption",
339+
"cycle", m.cycleNumber,
340+
"thread_id", info.threadID,
341+
"thread_url", threadURL)
342+
continue
343+
}
329344
if thread.ThreadTitle == "" {
330345
thread.ThreadTitle = title
331346
}
@@ -353,9 +368,15 @@ func (m *Monitor) fetchThreadPosts(
353368
}
354369

355370
// updateLastPolledForAllSubscribers updates LastPolledAt for all subscribers when no posts are found.
356-
func (*Monitor) updateLastPolledForAllSubscribers(info *threadCheckInfo, now time.Time) {
371+
func (m *Monitor) updateLastPolledForAllSubscribers(info *threadCheckInfo, now time.Time) {
357372
for _, sub := range info.subscribers {
358373
thread := sub.Threads[info.threadID]
374+
if thread == nil {
375+
m.logger.Error("CRITICAL: Thread not found when updating poll time - data corruption",
376+
"cycle", m.cycleNumber,
377+
"thread_id", info.threadID)
378+
continue
379+
}
359380
thread.LastPolledAt = now
360381
}
361382
}
@@ -378,10 +399,18 @@ func (m *Monitor) updateSubscriberTimestamps(thread *notifier.Thread, now, lates
378399
func (m *Monitor) saveInitialState(
379400
ctx context.Context,
380401
sub *notifier.Subscription,
381-
email, threadURL, postID string,
402+
email, threadID, threadURL, postID string,
382403
savedEmails map[string]bool,
383404
) bool {
384-
thread := sub.Threads[threadURL]
405+
thread := sub.Threads[threadID]
406+
if thread == nil {
407+
m.logger.Error("CRITICAL: Thread not found when saving initial state - data corruption",
408+
"cycle", m.cycleNumber,
409+
"email", email,
410+
"thread_id", threadID,
411+
"thread_url", threadURL)
412+
return false
413+
}
385414
thread.LastPostID = postID
386415

387416
m.logger.Info("Saving initial state for subscriber",
@@ -515,8 +544,16 @@ func (m *Monitor) sendNotificationAndSave(ctx context.Context, sub *notifier.Sub
515544
}
516545

517546
// saveStateNoNewPosts saves state when there are no new posts for a subscriber.
518-
func (m *Monitor) saveStateNoNewPosts(ctx context.Context, sub *notifier.Subscription, email, threadURL string, savedEmails map[string]bool) {
519-
thread := sub.Threads[threadURL]
547+
func (m *Monitor) saveStateNoNewPosts(ctx context.Context, sub *notifier.Subscription, email, threadID, threadURL string, savedEmails map[string]bool) {
548+
thread := sub.Threads[threadID]
549+
if thread == nil {
550+
m.logger.Error("CRITICAL: Thread not found when saving state (no new posts) - data corruption",
551+
"cycle", m.cycleNumber,
552+
"email", email,
553+
"thread_id", threadID,
554+
"thread_url", threadURL)
555+
return
556+
}
520557

521558
m.logger.Info("No new posts - saving state",
522559
"cycle", m.cycleNumber,

0 commit comments

Comments
 (0)