diff options
Diffstat (limited to 'backend')
| -rw-r--r-- | backend/cmd/serve.go | 62 | ||||
| -rw-r--r-- | backend/feed/feed.go | 75 | ||||
| -rw-r--r-- | backend/graphql/resolver/schema.resolvers.go | 33 |
3 files changed, 84 insertions, 86 deletions
diff --git a/backend/cmd/serve.go b/backend/cmd/serve.go index 30d0702..e2d2df5 100644 --- a/backend/cmd/serve.go +++ b/backend/cmd/serve.go @@ -4,7 +4,6 @@ import ( "context" "database/sql" "embed" - "fmt" "log" "net/http" "os" @@ -20,78 +19,23 @@ import ( "github.com/labstack/echo-contrib/session" "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" - "github.com/mmcdole/gofeed" "github.com/vektah/gqlparser/v2/ast" "undef.ninja/x/feedaka/auth" "undef.ninja/x/feedaka/config" "undef.ninja/x/feedaka/db" + "undef.ninja/x/feedaka/feed" "undef.ninja/x/feedaka/graphql" "undef.ninja/x/feedaka/graphql/resolver" ) func fetchOneFeed(feedID int64, url string, ctx context.Context, queries *db.Queries) error { log.Printf("Fetching %s...\n", url) - fp := gofeed.NewParser() - ctx, cancel := context.WithTimeout(ctx, 10*time.Second) - defer cancel() - feed, err := fp.ParseURLWithContext(url, ctx) - if err != nil { - return fmt.Errorf("Failed to fetch %s: %v\n", url, err) - } - err = queries.UpdateFeedMetadata(ctx, db.UpdateFeedMetadataParams{ - Title: feed.Title, - FetchedAt: time.Now().UTC().Format(time.RFC3339), - ID: feedID, - }) - if err != nil { - return err - } - // Get GUIDs for this feed (for updating existing articles) - guids, err := queries.GetArticleGUIDsByFeed(ctx, feedID) + f, err := feed.Fetch(ctx, url) if err != nil { return err } - existingFeedGUIDs := make(map[string]bool) - for _, guid := range guids { - existingFeedGUIDs[guid] = true - } - for _, item := range feed.Items { - if existingFeedGUIDs[item.GUID] { - // Article exists in this feed, update it - err := queries.UpdateArticle(ctx, db.UpdateArticleParams{ - Title: item.Title, - Url: item.Link, - FeedID: feedID, - Guid: item.GUID, - }) - if err != nil { - return err - } - } else { - // Check if article with same GUID exists globally (in any feed) - exists, err := queries.CheckArticleExistsByGUID(ctx, item.GUID) - if err != nil { - return err - } - if exists == 1 { - // Article already exists in another feed, skip - continue - } - // Create new article - _, err = queries.CreateArticle(ctx, db.CreateArticleParams{ - FeedID: feedID, - Guid: item.GUID, - Title: item.Title, - Url: item.Link, - IsRead: 0, - }) - if err != nil { - return err - } - } - } - return nil + return feed.Sync(ctx, queries, feedID, f) } func listFeedsToBeFetched(ctx context.Context, queries *db.Queries) (map[int64]string, error) { diff --git a/backend/feed/feed.go b/backend/feed/feed.go new file mode 100644 index 0000000..4349d1e --- /dev/null +++ b/backend/feed/feed.go @@ -0,0 +1,75 @@ +package feed + +import ( + "context" + "fmt" + "time" + + "github.com/mmcdole/gofeed" + + "undef.ninja/x/feedaka/db" +) + +func Fetch(ctx context.Context, url string) (*gofeed.Feed, error) { + fp := gofeed.NewParser() + ctx, cancel := context.WithTimeout(ctx, 10*time.Second) + defer cancel() + feed, err := fp.ParseURLWithContext(url, ctx) + if err != nil { + return nil, fmt.Errorf("failed to fetch %s: %w", url, err) + } + return feed, nil +} + +func Sync(ctx context.Context, queries *db.Queries, feedID int64, f *gofeed.Feed) error { + err := queries.UpdateFeedMetadata(ctx, db.UpdateFeedMetadataParams{ + Title: f.Title, + FetchedAt: time.Now().UTC().Format(time.RFC3339), + ID: feedID, + }) + if err != nil { + return err + } + + guids, err := queries.GetArticleGUIDsByFeed(ctx, feedID) + if err != nil { + return err + } + existingFeedGUIDs := make(map[string]bool, len(guids)) + for _, guid := range guids { + existingFeedGUIDs[guid] = true + } + + for _, item := range f.Items { + if existingFeedGUIDs[item.GUID] { + err := queries.UpdateArticle(ctx, db.UpdateArticleParams{ + Title: item.Title, + Url: item.Link, + FeedID: feedID, + Guid: item.GUID, + }) + if err != nil { + return err + } + } else { + exists, err := queries.CheckArticleExistsByGUID(ctx, item.GUID) + if err != nil { + return err + } + if exists == 1 { + continue + } + _, err = queries.CreateArticle(ctx, db.CreateArticleParams{ + FeedID: feedID, + Guid: item.GUID, + Title: item.Title, + Url: item.Link, + IsRead: 0, + }) + if err != nil { + return err + } + } + } + return nil +} diff --git a/backend/graphql/resolver/schema.resolvers.go b/backend/graphql/resolver/schema.resolvers.go index c3f6f0a..10f892f 100644 --- a/backend/graphql/resolver/schema.resolvers.go +++ b/backend/graphql/resolver/schema.resolvers.go @@ -11,9 +11,9 @@ import ( "strconv" "time" - "github.com/mmcdole/gofeed" "undef.ninja/x/feedaka/auth" "undef.ninja/x/feedaka/db" + "undef.ninja/x/feedaka/feed" gql "undef.ninja/x/feedaka/graphql" "undef.ninja/x/feedaka/graphql/model" ) @@ -26,8 +26,7 @@ func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed } // Fetch the feed to get its title - fp := gofeed.NewParser() - feed, err := fp.ParseURL(url) + f, err := feed.Fetch(ctx, url) if err != nil { return nil, fmt.Errorf("failed to parse feed: %w", err) } @@ -35,7 +34,7 @@ func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed // Insert the feed into the database dbFeed, err := r.Queries.CreateFeed(ctx, db.CreateFeedParams{ Url: url, - Title: feed.Title, + Title: f.Title, FetchedAt: time.Now().UTC().Format(time.RFC3339), UserID: userID, }) @@ -43,29 +42,9 @@ func (r *mutationResolver) AddFeed(ctx context.Context, url string) (*model.Feed return nil, fmt.Errorf("failed to insert feed: %w", err) } - // Insert articles from the feed (skip duplicates by guid) - for _, item := range feed.Items { - // Check if article with same GUID already exists globally - exists, err := r.Queries.CheckArticleExistsByGUID(ctx, item.GUID) - if err != nil { - fmt.Printf("Failed to check article existence: %v\n", err) - continue - } - if exists == 1 { - // Article already exists, skip - continue - } - _, err = r.Queries.CreateArticle(ctx, db.CreateArticleParams{ - FeedID: dbFeed.ID, - Guid: item.GUID, - Title: item.Title, - Url: item.Link, - IsRead: 0, - }) - if err != nil { - // Log but don't fail on individual article errors - fmt.Printf("Failed to insert article: %v\n", err) - } + // Sync articles from the feed + if err := feed.Sync(ctx, r.Queries, dbFeed.ID, f); err != nil { + return nil, fmt.Errorf("failed to sync articles: %w", err) } return &model.Feed{ |
