Skip to content

Commit a03dc75

Browse files
Thomas StrombergThomas Stromberg
authored andcommitted
lint
1 parent cbae3c3 commit a03dc75

File tree

9 files changed

+31
-77
lines changed

9 files changed

+31
-77
lines changed

email/provider.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
package email
33

44
import (
5+
"advrider-notifier/pkg/notifier"
56
"context"
67
"log/slog"
7-
8-
"advrider-notifier/pkg/notifier"
98
)
109

1110
// Provider defines the interface for email sending implementations.

email/templates.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package email
22

33
import (
4+
"advrider-notifier/pkg/notifier"
45
"fmt"
56
"net/url"
67
"strings"
78
"time"
8-
9-
"advrider-notifier/pkg/notifier"
109
)
1110

1211
func (s *Sender) formatNotificationBody(sub *notifier.Subscription, thread *notifier.Thread, posts []*notifier.Post) string {
@@ -96,7 +95,6 @@ func (s *Sender) formatNotificationBody(sub *notifier.Subscription, thread *noti
9695

9796
// Footer with thread link and manage link
9897
// Always add grey border to separate footer from content
99-
//nolint:gocritic // %q would add extra quotes in HTML context
10098
b.WriteString("<div class=\"footer with-border\">\n")
10199

102100
// Link to the last page with anchor to latest post (e.g., .../page-12#post-12345)
@@ -373,5 +371,5 @@ func isSafeURL(urlStr string) bool {
373371
strings.HasPrefix(urlStr, "/") ||
374372
strings.HasPrefix(urlStr, "./") ||
375373
strings.HasPrefix(urlStr, "../") ||
376-
(!strings.Contains(urlStr, ":") && len(urlStr) > 0) // relative path without protocol
374+
(!strings.Contains(urlStr, ":") && urlStr != "") // relative path without protocol
377375
}

main.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@
33
package main
44

55
import (
6+
"advrider-notifier/email"
7+
"advrider-notifier/poll"
8+
"advrider-notifier/scraper"
9+
"advrider-notifier/server"
10+
"advrider-notifier/storage"
611
"context"
712
"embed"
813
"log/slog"
@@ -11,12 +16,6 @@ import (
1116
"strings"
1217
"time"
1318

14-
"advrider-notifier/email"
15-
"advrider-notifier/poll"
16-
"advrider-notifier/scraper"
17-
"advrider-notifier/server"
18-
"advrider-notifier/storage"
19-
2019
gcs "cloud.google.com/go/storage"
2120
"github.com/codeGROOVE-dev/gsm"
2221
)
@@ -41,7 +40,9 @@ func main() {
4140
// Load SALT from GSM or environment variable
4241
salt := secret(ctx, "SALT", logger)
4342
if salt == "" {
44-
logger.Error("SALT is not set in environment or GSM - subscription unsubscribe URLs will be guessable, allowing anyone to unsubscribe any email address. This is a CRITICAL SECURITY ISSUE.")
43+
logger.Error("SALT is not set in environment or GSM",
44+
"issue", "subscription unsubscribe URLs will be guessable",
45+
"severity", "CRITICAL SECURITY ISSUE - anyone can unsubscribe any email address")
4546
os.Exit(1)
4647
}
4748

main_test.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
package main
22

33
import (
4+
"advrider-notifier/email"
5+
"advrider-notifier/pkg/notifier"
46
"log/slog"
57
"os"
68
"strings"
79
"testing"
8-
9-
"advrider-notifier/email"
10-
"advrider-notifier/pkg/notifier"
1110
)
1211

1312
func TestGetText(t *testing.T) {

poll/poll.go

Lines changed: 11 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,13 @@
22
package poll
33

44
import (
5+
"advrider-notifier/pkg/notifier"
56
"context"
67
"fmt"
78
"log/slog"
89
"math"
910
"sync"
1011
"time"
11-
12-
"advrider-notifier/pkg/notifier"
1312
)
1413

1514
const maxPostsPerEmail = 10 // Safety limit: max posts to include in a single email
@@ -278,12 +277,17 @@ func (m *Monitor) checkThreadForSubscribers(
278277
// Update poll time and latest post time for this subscriber
279278
m.updateSubscriberTimestamps(thread, now, latestPostTime, email, threadURL)
280279

281-
// First check for this subscriber - just record the latest post ID
280+
// First check for this subscriber - just record the latest post ID without notification
282281
if thread.LastPostID == "" {
283-
if m.saveInitialState(ctx, sub, email, info.threadID, threadURL, latestPost.ID, savedEmails) {
284-
hasUpdates = true
285-
}
286-
continue
282+
thread.LastPostID = latestPost.ID
283+
m.logger.Info("New subscriber - recording initial state without notification",
284+
"cycle", m.cycleNumber,
285+
"email", email,
286+
"thread_url", threadURL,
287+
"thread_title", thread.ThreadTitle,
288+
"initial_post_id", latestPost.ID)
289+
m.saveStateNoNewPosts(ctx, sub, email, info.threadID, threadURL, savedEmails)
290+
continue // Move to next subscriber (other subscribers will still be notified)
287291
}
288292

289293
// Find new posts for this subscriber
@@ -395,49 +399,6 @@ func (m *Monitor) updateSubscriberTimestamps(thread *notifier.Thread, now, lates
395399
}
396400
}
397401

398-
// saveInitialState saves the initial state for a new subscriber.
399-
func (m *Monitor) saveInitialState(
400-
ctx context.Context,
401-
sub *notifier.Subscription,
402-
email, threadID, threadURL, postID string,
403-
savedEmails map[string]bool,
404-
) bool {
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-
}
414-
thread.LastPostID = postID
415-
416-
m.logger.Info("Saving initial state for subscriber",
417-
"cycle", m.cycleNumber,
418-
"email", email,
419-
"thread_url", threadURL,
420-
"post_id", postID)
421-
422-
if err := m.store.Save(ctx, sub); err != nil {
423-
m.logger.Error("Failed to save initial state for subscriber",
424-
"cycle", m.cycleNumber,
425-
"email", email,
426-
"thread_url", threadURL,
427-
"thread_title", thread.ThreadTitle,
428-
"error", err)
429-
} else {
430-
savedEmails[email] = true
431-
m.logger.Info("Initial post ID recorded and saved",
432-
"cycle", m.cycleNumber,
433-
"email", email,
434-
"thread_url", threadURL,
435-
"thread_title", thread.ThreadTitle,
436-
"post_id", postID)
437-
}
438-
return true
439-
}
440-
441402
// findNewPosts identifies new posts for a subscriber since their last seen post.
442403
func (m *Monitor) findNewPosts(posts []*notifier.Post, thread *notifier.Thread, email, threadURL string) []*notifier.Post {
443404
var newPosts []*notifier.Post

scraper/scraper.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package scraper
33

44
import (
5+
"advrider-notifier/pkg/notifier"
56
"context"
67
"errors"
78
"fmt"
@@ -10,8 +11,6 @@ import (
1011
"strings"
1112
"time"
1213

13-
"advrider-notifier/pkg/notifier"
14-
1514
"github.com/PuerkitoBio/goquery"
1615
"github.com/codeGROOVE-dev/retry"
1716
)
@@ -69,14 +68,14 @@ func (s *Scraper) LatestPost(ctx context.Context, threadURL string) (*notifier.P
6968
// SmartFetch fetches posts efficiently using multi-page strategy.
7069
// Returns posts, title, and error.
7170
func (s *Scraper) SmartFetch(ctx context.Context, threadURL string, lastSeenPostID string) ([]*notifier.Post, string, error) {
72-
page, err := s.smartFetch(ctx, threadURL, lastSeenPostID)
71+
page, err := s.fetchWithStrategy(ctx, threadURL, lastSeenPostID)
7372
if err != nil {
7473
return nil, "", err
7574
}
7675
return page.Posts, page.Title, nil
7776
}
7877

79-
func (s *Scraper) smartFetch(ctx context.Context, threadURL string, lastSeenPostID string) (*Page, error) {
78+
func (s *Scraper) fetchWithStrategy(ctx context.Context, threadURL string, lastSeenPostID string) (*Page, error) {
8079
s.logger.Info("Starting smart thread fetch", "url", threadURL, "last_seen_post", lastSeenPostID)
8180

8281
// Step 1: Fetch first page to get title and last page number

server/server.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package server
33

44
import (
5+
"advrider-notifier/pkg/notifier"
56
"context"
67
"embed"
78
"errors"
@@ -16,8 +17,6 @@ import (
1617
"strings"
1718
"sync"
1819
"time"
19-
20-
"advrider-notifier/pkg/notifier"
2120
)
2221

2322
//go:embed tmpl/*.tmpl

server/subscribe.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
package server
22

33
import (
4+
"advrider-notifier/pkg/notifier"
5+
"advrider-notifier/poll"
46
"context"
57
"fmt"
68
"net/http"
79
"strings"
810
"time"
9-
10-
"advrider-notifier/pkg/notifier"
11-
"advrider-notifier/poll"
1211
)
1312

1413
//nolint:maintidx,funlen,varnamelen // HTTP handler with comprehensive validation - complexity justified for security

storage/storage.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
package storage
33

44
import (
5+
"advrider-notifier/pkg/notifier"
56
"context"
67
"crypto/hmac"
78
"crypto/sha256"
@@ -15,8 +16,6 @@ import (
1516
"path/filepath"
1617
"strings"
1718

18-
"advrider-notifier/pkg/notifier"
19-
2019
"cloud.google.com/go/storage"
2120
"google.golang.org/api/iterator"
2221
)

0 commit comments

Comments
 (0)