diff options
| author | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
|---|---|---|
| committer | Fuwn <[email protected]> | 2026-02-07 01:42:57 -0800 |
| commit | 5c5b1993edd890a80870ee05607ac5f088191d4e (patch) | |
| tree | a721b76bcd49ba10826c53efc87302c7a689512f /services/worker/internal/scheduler/refresh.go | |
| download | asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.tar.xz asa.news-5c5b1993edd890a80870ee05607ac5f088191d4e.zip | |
feat: asa.news RSS reader with developer tier, REST API, and webhooks
Full-stack RSS reader SaaS: Supabase + Next.js + Go worker.
Includes three subscription tiers (free/pro/developer), API key auth,
read-only REST API, webhook push notifications, Stripe billing with
proration, and PWA support.
Diffstat (limited to 'services/worker/internal/scheduler/refresh.go')
| -rw-r--r-- | services/worker/internal/scheduler/refresh.go | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/services/worker/internal/scheduler/refresh.go b/services/worker/internal/scheduler/refresh.go new file mode 100644 index 0000000..8271fa0 --- /dev/null +++ b/services/worker/internal/scheduler/refresh.go @@ -0,0 +1,196 @@ +package scheduler + +import ( + "context" + "github.com/Fuwn/asa-news/internal/fetcher" + "github.com/Fuwn/asa-news/internal/model" + "github.com/Fuwn/asa-news/internal/parser" + "github.com/Fuwn/asa-news/internal/webhook" + "github.com/Fuwn/asa-news/internal/writer" + "log/slog" +) + +func ProcessRefreshRequest( + refreshContext context.Context, + feed model.Feed, + feedFetcher *fetcher.Fetcher, + feedParser *parser.Parser, + feedWriter *writer.Writer, + webhookDispatcher *webhook.Dispatcher, + logger *slog.Logger, +) { + logger.Info( + "processing feed refresh", + "feed_identifier", feed.Identifier, + "feed_url", feed.URL, + ) + + var ownerIdentifier *string + + if feed.Visibility == "authenticated" { + logger.Warn( + "authenticated feed refresh not yet implemented", + "feed_identifier", feed.Identifier, + ) + + return + } + + authenticationConfig := fetcher.AuthenticationConfiguration{} + + entityTag := "" + + if feed.EntityTag != nil { + entityTag = *feed.EntityTag + } + + lastModified := "" + + if feed.LastModified != nil { + lastModified = *feed.LastModified + } + + fetchResult, fetchError := feedFetcher.Fetch( + refreshContext, + feed.URL, + entityTag, + lastModified, + authenticationConfig, + ) + + if fetchError != nil { + logger.Warn( + "feed fetch failed", + "feed_identifier", feed.Identifier, + "error", fetchError, + ) + + recordError := feedWriter.RecordFeedError(refreshContext, feed.Identifier, fetchError.Error()) + + if recordError != nil { + logger.Error( + "failed to record feed error", + "feed_identifier", feed.Identifier, + "error", recordError, + ) + } + + return + } + + if fetchResult.NotModified { + logger.Info( + "feed not modified", + "feed_identifier", feed.Identifier, + ) + + metadataError := feedWriter.UpdateFeedMetadata( + refreshContext, + feed.Identifier, + fetchResult.EntityTag, + fetchResult.LastModifiedHeader, + "", + "", + ) + + if metadataError != nil { + logger.Error( + "failed to update feed metadata after not-modified", + "feed_identifier", feed.Identifier, + "error", metadataError, + ) + } + + return + } + + parseResult, parseError := feedParser.Parse(feed.Identifier, ownerIdentifier, fetchResult.Body) + + if parseError != nil { + logger.Warn( + "feed parse failed", + "feed_identifier", feed.Identifier, + "error", parseError, + ) + + recordError := feedWriter.RecordFeedError(refreshContext, feed.Identifier, parseError.Error()) + + if recordError != nil { + logger.Error( + "failed to record parse error", + "feed_identifier", feed.Identifier, + "error", recordError, + ) + } + + return + } + + rowsAffected, writeError := feedWriter.WriteEntries(refreshContext, parseResult.Entries) + + if writeError != nil { + logger.Error( + "failed to write feed entries", + "feed_identifier", feed.Identifier, + "error", writeError, + ) + + recordError := feedWriter.RecordFeedError(refreshContext, feed.Identifier, writeError.Error()) + + if recordError != nil { + logger.Error( + "failed to record write error", + "feed_identifier", feed.Identifier, + "error", recordError, + ) + } + + return + } + + if rowsAffected > 0 && webhookDispatcher != nil { + webhookDispatcher.DispatchForFeed(refreshContext, feed.Identifier, parseResult.Entries) + } + + metadataError := feedWriter.UpdateFeedMetadata( + refreshContext, + feed.Identifier, + fetchResult.EntityTag, + fetchResult.LastModifiedHeader, + parseResult.FeedTitle, + parseResult.SiteURL, + ) + + if metadataError != nil { + logger.Error( + "failed to update feed metadata", + "feed_identifier", feed.Identifier, + "error", metadataError, + ) + } + + if parseResult.AudioEnclosureRatio > 0.5 { + if feedTypeError := feedWriter.UpdateFeedType(refreshContext, feed.Identifier, "podcast"); feedTypeError != nil { + logger.Error( + "failed to update feed type to podcast", + "feed_identifier", feed.Identifier, + "error", feedTypeError, + ) + } + } else if feed.FeedType != nil && *feed.FeedType == "podcast" { + if feedTypeError := feedWriter.UpdateFeedType(refreshContext, feed.Identifier, "rss"); feedTypeError != nil { + logger.Error( + "failed to clear podcast feed type", + "feed_identifier", feed.Identifier, + "error", feedTypeError, + ) + } + } + + logger.Info( + "feed refresh completed", + "feed_identifier", feed.Identifier, + "entries_parsed", len(parseResult.Entries), + "rows_affected", rowsAffected, + ) +} |